From 6f97fd5f7c528594899e66d2d8b502a20ade6f53 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 13:29:07 -0700 Subject: Revert iconics updates for now --- buildSrc/src/main/kotlin/kau/Dependencies.kt | 6 +++--- buildSrc/src/main/kotlin/kau/Versions.kt | 4 ++-- docs/Migration.md | 4 ---- .../main/kotlin/ca/allanwang/kau/mediapicker/BlurredImageView.kt | 2 +- .../src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt | 2 +- .../kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt | 2 +- .../src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt | 2 +- sample/src/main/kotlin/ca/allanwang/kau/sample/AdapterActivity.kt | 2 +- sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt | 2 +- .../src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt | 2 +- .../src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt | 2 +- 11 files changed, 13 insertions(+), 17 deletions(-) diff --git a/buildSrc/src/main/kotlin/kau/Dependencies.kt b/buildSrc/src/main/kotlin/kau/Dependencies.kt index c704500..a6268ae 100644 --- a/buildSrc/src/main/kotlin/kau/Dependencies.kt +++ b/buildSrc/src/main/kotlin/kau/Dependencies.kt @@ -24,9 +24,9 @@ object Dependencies { const val googleMaterial = "com.google.android.material:material:${Versions.googleMaterial}" const val iconics = "com.mikepenz:iconics-core:${Versions.iconics}" - const val iconicsGoogle = "com.mikepenz:google-material-typeface:${Versions.iconicsGoogle}.original-kotlin@aar" - const val iconicsMaterial = "com.mikepenz:material-design-iconic-typeface:${Versions.iconicsMaterial}-kotlin@aar" - const val iconicsCommunity = "com.mikepenz:community-material-typeface:${Versions.iconicsCommunity}-kotlin@aar" + const val iconicsGoogle = "com.mikepenz:google-material-typeface:${Versions.iconicsGoogle}.original@aar" + const val iconicsMaterial = "com.mikepenz:material-design-iconic-typeface:${Versions.iconicsMaterial}@aar" + const val iconicsCommunity = "com.mikepenz:community-material-typeface:${Versions.iconicsCommunity}@aar" const val aboutLibraries = "com.mikepenz:aboutlibraries:${Versions.aboutLibraries}" diff --git a/buildSrc/src/main/kotlin/kau/Versions.kt b/buildSrc/src/main/kotlin/kau/Versions.kt index e953929..7133ed5 100644 --- a/buildSrc/src/main/kotlin/kau/Versions.kt +++ b/buildSrc/src/main/kotlin/kau/Versions.kt @@ -43,8 +43,8 @@ object Versions { // https://github.com/mikepenz/Android-Iconics#1-provide-the-gradle-dependency const val iconics = "3.2.5" - const val iconicsGoogle = "3.0.1.4" - const val iconicsMaterial = "2.2.0.6" + const val iconicsGoogle = "3.0.1.3" + const val iconicsMaterial = "2.2.0.5" const val iconicsCommunity = "3.5.95.1" // https://github.com/afollestad/material-dialogs/releases diff --git a/docs/Migration.md b/docs/Migration.md index 58667ca..a7162fa 100644 --- a/docs/Migration.md +++ b/docs/Migration.md @@ -29,10 +29,6 @@ For instance, instead of using `kauDependencies`, we now use `kau.Dependencies`. There is also no longer a need to use `apply plugin 'ca.allanwang.kau'`; adding the plugin to the classpath suffices. -## Iconics - -With an iconics update, most of the package names have changed. - # v4.0.1-alpha02 * `kauParseFaq` is now synchronous. diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/BlurredImageView.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/BlurredImageView.kt index 4342229..739bf47 100644 --- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/BlurredImageView.kt +++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/BlurredImageView.kt @@ -28,7 +28,7 @@ import ca.allanwang.kau.utils.scaleXY import ca.allanwang.kau.utils.setBackgroundColorRes import ca.allanwang.kau.utils.setIcon import ca.allanwang.kau.utils.visible -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial import jp.wasabeef.blurry.internal.BlurFactor import jp.wasabeef.blurry.internal.BlurTask import kotlinx.android.synthetic.main.kau_blurred_imageview.view.* diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt index 8d2dab9..1941ec0 100644 --- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt +++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaActionItem.kt @@ -26,8 +26,8 @@ import ca.allanwang.kau.permissions.PERMISSION_WRITE_EXTERNAL_STORAGE import ca.allanwang.kau.permissions.kauRequestPermissions import ca.allanwang.kau.utils.materialDialog import ca.allanwang.kau.utils.string +import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial import java.io.File /** diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt index fa161c7..5e5d1ed 100644 --- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt +++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerActivityBase.kt @@ -26,7 +26,7 @@ import ca.allanwang.kau.utils.setIcon import ca.allanwang.kau.utils.toDrawable import ca.allanwang.kau.utils.toast import com.google.android.material.appbar.AppBarLayout -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial import kotlinx.android.synthetic.main.kau_activity_image_picker.* /** diff --git a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt index fe7a3b1..e6556ab 100644 --- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt +++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt @@ -46,7 +46,7 @@ import com.bumptech.glide.Glide import com.bumptech.glide.RequestManager import com.mikepenz.fastadapter.IItem import com.mikepenz.fastadapter.adapters.ItemAdapter -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon import kotlinx.coroutines.CancellationException diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/AdapterActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/AdapterActivity.kt index a851c1d..a11a672 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/AdapterActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/AdapterActivity.kt @@ -22,7 +22,7 @@ import ca.allanwang.kau.ui.activities.ElasticRecyclerActivity import ca.allanwang.kau.utils.toast import com.mikepenz.fastadapter.IItem import com.mikepenz.fastadapter.adapters.ItemAdapter -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial /** * Created by Allan Wang on 2017-07-17. diff --git a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt index 73d6eab..9ce3f42 100644 --- a/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt +++ b/sample/src/main/kotlin/ca/allanwang/kau/sample/MainActivity.kt @@ -39,7 +39,7 @@ import ca.allanwang.kau.utils.toast import ca.allanwang.kau.utils.withSceneTransitionAnimation import ca.allanwang.kau.xml.showChangelog import com.afollestad.materialdialogs.input.input -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial class MainActivity : KPrefActivity() { diff --git a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt index 98921bb..7747d81 100644 --- a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt +++ b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchItem.kt @@ -32,7 +32,7 @@ import ca.allanwang.kau.utils.setIcon import ca.allanwang.kau.utils.setRippleBackground import ca.allanwang.kau.utils.visible import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial /** * Created by Allan Wang on 2017-06-23. diff --git a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt index e4c6dbd..10f2de3 100644 --- a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt +++ b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt @@ -64,7 +64,7 @@ import ca.allanwang.kau.utils.visible import ca.allanwang.kau.utils.withLinearAdapter import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter import com.mikepenz.iconics.typeface.IIcon -import com.mikepenz.iconics.typeface.library.googlematerial.GoogleMaterial +import com.mikepenz.google_material_typeface_library.GoogleMaterial import kotlinx.android.synthetic.main.kau_search_view.view.* /** -- cgit v1.2.3 From e1849c9c633047979d8017262041e925ec3bd219 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 13:51:52 -0700 Subject: Update to sdk 29 and update changelog --- buildSrc/src/main/kotlin/kau/Versions.kt | 4 ++-- core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt | 2 +- core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt | 2 +- docs/Changelog.md | 3 +++ sample/src/main/res/xml/kau_changelog.xml | 5 +++-- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/buildSrc/src/main/kotlin/kau/Versions.kt b/buildSrc/src/main/kotlin/kau/Versions.kt index 7133ed5..515f702 100644 --- a/buildSrc/src/main/kotlin/kau/Versions.kt +++ b/buildSrc/src/main/kotlin/kau/Versions.kt @@ -3,10 +3,10 @@ package kau object Versions { const val coreMinSdk = 19 const val minSdk = 21 - const val targetSdk = 28 + const val targetSdk = 29 // https://developer.android.com/studio/releases/build-tools - const val buildTools = "28.0.3" + const val buildTools = "29.0.0" // https://mvnrepository.com/artifact/androidx.appcompat/appcompat?repo=google const val appcompat = "1.0.2" diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt index 1070e11..3f8fe28 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt @@ -60,7 +60,7 @@ internal object KPrefFloatTransaction : KPrefTransaction { internal object KPrefStringTransaction : KPrefTransaction { override fun get(prefs: SharedPreferences, key: String, fallback: String) = - prefs.getString(key, fallback) + prefs.getString(key, fallback) ?: "" override fun set(editor: SharedPreferences.Editor, key: String, data: String) { editor.putString(key, data) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt index e8680dc..d002fb8 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -239,7 +239,7 @@ fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || Con fun Context.copyToClipboard(text: String?, label: String = "Copied Text", showToast: Boolean = true) { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.primaryClip = ClipData.newPlainText(label, text ?: "") + clipboard.setPrimaryClip(ClipData.newPlainText(label, text ?: "")) if (showToast) toast(R.string.kau_text_copied) } diff --git a/docs/Changelog.md b/docs/Changelog.md index 61e5fb7..689fbd9 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -1,8 +1,11 @@ # Changelog ## v5.0.0 +* Update Android SDK to 29 and Kotlin to 1.3.31 * :core: Update Material Dialogs to 3.x +* :core: Change ProgressAnimator API * :colorpicker: Strip down to just the interface; unless you require the accent palette, it may be fine to just use MD's color extension +* :gradle-plugin: Convert to kotlin, rework dependencies, and remove extension hooks ## v4.1.0 * :core: Deprecate NetworkUtils, as the underlying functions are deprecated diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml index 53265d0..b5ec47c 100644 --- a/sample/src/main/res/xml/kau_changelog.xml +++ b/sample/src/main/res/xml/kau_changelog.xml @@ -7,10 +7,11 @@ --> + + - - + -- cgit v1.2.3 From a3589553aa19e570f5df34c143afd5dead32e9ef Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 14:07:53 -0700 Subject: Apply spotless --- .../kotlin/ca/allanwang/kau/colorpicker/CircleView.kt | 2 +- .../ca/allanwang/kau/colorpicker/ColorPickerDialog.kt | 2 +- .../kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt | 18 ++++++++++++++++-- .../allanwang/kau/kpref/activity/KPrefItemActions.kt | 17 ++++++++++++++++- 4 files changed, 34 insertions(+), 5 deletions(-) diff --git a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt index e748677..29257d8 100644 --- a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt +++ b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/CircleView.kt @@ -238,4 +238,4 @@ class CircleView @JvmOverloads constructor(context: Context, attrs: AttributeSet @ColorInt fun shiftColorUp(@ColorInt color: Int): Int = shiftColor(color, 1.1f) } -} \ No newline at end of file +} diff --git a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt index 02a1fff..487ed76 100644 --- a/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt +++ b/colorpicker/src/main/kotlin/ca/allanwang/kau/colorpicker/ColorPickerDialog.kt @@ -63,4 +63,4 @@ fun MaterialDialog.kauColorChooser(c: ColorContract) { selection = c.callback ) positiveButton(R.string.kau_done) -} \ No newline at end of file +} diff --git a/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt index 60f8680..6b99b14 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/ui/ProgressAnimatorTest.kt @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package ca.allanwang.kau.ui import org.junit.Test @@ -71,5 +86,4 @@ class ProgressAnimatorTest { assertTrue(called) assertTrue(i > 0.5f) } - -} \ No newline at end of file +} diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefItemActions.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefItemActions.kt index e28bac6..c1cdc15 100644 --- a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefItemActions.kt +++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefItemActions.kt @@ -1,3 +1,18 @@ +/* + * Copyright 2019 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package ca.allanwang.kau.kpref.activity /** @@ -8,4 +23,4 @@ interface KPrefItemActions { * Attempts to reload current item by identifying it with its titleId */ fun reloadSelf() -} \ No newline at end of file +} -- cgit v1.2.3 From 20e7919f0b14ee21107f14933a05e2e20e00780b Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 14:12:07 -0700 Subject: Open ctxCoroutine --- core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt index 57a9921..4d6ee54 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt @@ -18,6 +18,7 @@ package ca.allanwang.kau.utils import android.content.Context import android.os.Handler import android.os.Looper +import ca.allanwang.kau.internal.KauBaseActivity import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineStart import kotlinx.coroutines.android.asCoroutineDispatcher @@ -43,11 +44,11 @@ object ContextHelper : CoroutineScope { } /** - * Most context items implement [CoroutineScope] by default. + * Most context items implement [CoroutineScope] by default (through [KauBaseActivity]). * We will add a fallback just in case. * It is expected that the scope returned always has the Android main dispatcher as part of the context. */ -internal inline val Context.ctxCoroutine: CoroutineScope +inline val Context.ctxCoroutine: CoroutineScope get() = this as? CoroutineScope ?: ContextHelper /** -- cgit v1.2.3 From 580200f1cacdb6feea99686cf9804ba3e0ed1c60 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 19:00:44 -0700 Subject: Update sdk in travis --- .travis.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7bd509f..6f0681f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,9 +3,9 @@ jdk: - oraclejdk8 env: global: - - ANDROID_API=28 + - ANDROID_API=29 - EMULATOR_API=19 - - ANDROID_BUILD_TOOLS=28.0.3 + - ANDROID_BUILD_TOOLS=29.0.0 git: depth: 500 android: -- cgit v1.2.3 From 743278b11ba1f3e8449ed31531e37fc4f9a65d34 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 19:04:51 -0700 Subject: Encrypt slack room so forks don't use it --- .travis.yml | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6f0681f..7f4d045 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,28 +15,15 @@ android: - tools - build-tools-$ANDROID_BUILD_TOOLS - android-$ANDROID_API -# - android-$EMULATOR_API - extra-android-m2repository - extra-google-m2repository -# - sys-img-armeabi-v7a-addon-google_apis-google-$ANDROID_API_LEVEL -# - sys-img-armeabi-v7a-addon-google_apis-google-$EMULATOR_API_LEVEL licenses: - ".+" -#before_script: -#- echo "y" | android update sdk -a --no-ui --filter android-$EMULATOR_API -#- echo "y" | android update sdk -a --no-ui --filter sys-img-armeabi-v7a-android-$EMULATOR_API -#- android list targets | grep -E '^id:' | awk -F '"' '{$1=""; print $2}' # list all targets -#- echo no | android create avd --force -n test -t android-$EMULATOR_API --abi armeabi-v7a -#- emulator -avd test -skin -no-audio -no-window & -#- android-wait-for-emulator -#- adb shell settings put global window_animation_scale 0 & -#- adb shell settings put global transition_animation_scale 0 & -#- adb shell settings put global animator_duration_scale 0 & -#- adb shell input keyevent 82 & script: - chmod +x gradlew - "./gradlew --quiet androidGitVersion" -- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew lintRelease publishRelease; else ./gradlew lintRelease test; fi +- if [[ "$TRAVIS_BRANCH" == "master" ]]; then ./gradlew lintRelease publishRelease; + else ./gradlew lintRelease test; fi branches: except: - gh-pages @@ -45,17 +32,17 @@ notifications: email: false slack: rooms: - - pitchedapps:G5OB9U1vsDxy9mxt0Nt6gbFu#kau + secure: kt/n/Mn7qbRKg6OXFLY2oxon9mjdrowuGnOavGTsXr7OQDKNp/PMSanIhxYJwkU8at5jO2Zlyjh/orksBGy6ERX4+AMbTWlDH0n/F5E7hHwxDABBup78VgU+pBgbdUrF5taUkrODcl3ytP3Pwx5MfSrdw5v6IKR0P00Tpwo0paeurBDp9XnG2DhOn3diJNH1YpdHuIKoPnLORFnUkTAWdGqQkfQFY64idbD9JO+/KODG7Y1KDtrWgg4x8FrOLAaVRQddISxyBpOoCiThF3k8nbQztAwhDyJWber0xWNuTwlKKfTFQvxkUkjb55jxRePFQTmgw/u+ip/fnBfVx9l/BAc1Rj2vRVRoAWfmWaBRQU05lZHeI4tvuuBmBxhT2qHeexdXYxPOugFHrlG34u6m1ZiSOQ5mcOxmMFaK2wjonL6haTqN5Uzm7ZX3y6LzIihZi5SzOAnfjUzGpEkw3uh+h7tNRXNpu4uk//GTd2gO/CHlIr+6Hm70RihN32ns5++EmbyP3rJ/6Xwk1tYJMRIJWpU+8kdadm9DZmsB1TPb/u9Zjdaq2pdkcyP5huZvZIdcPjiCkAiSuwtF+pxjd5Ygch7U/W/H73zDwo7dKknt40DOdv+1oiPstiaoDCgLvWiMDaszG15YKlTx8ggbxbLZZapriOc2u9F/XmQwNiZmqMo= on_success: always on_failure: always cache: directories: - - $HOME/.m2 - - $HOME/.gradle/caches/ - - $HOME/.gradle/wrapper/ - - $HOME/.android/build-cache + - "$HOME/.m2" + - "$HOME/.gradle/caches/" + - "$HOME/.gradle/wrapper/" + - "$HOME/.android/build-cache" before_install: - yes | sdkmanager "platforms;android-$ANDROID_API" - openssl aes-256-cbc -K $encrypted_12e8842891a3_key -iv $encrypted_12e8842891a3_iv -in files/kau.tar.enc -out kau.tar -d -- tar xvf kau.tar \ No newline at end of file +- tar xvf kau.tar -- cgit v1.2.3 From 69e3341677d92f699e4dc1202ca83056ed7b1b86 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 20:27:07 -0700 Subject: Remove reflection usage, resolves #176 --- .../kotlin/ca/allanwang/kau/utils/ColorUtils.kt | 32 ++++------------------ 1 file changed, 5 insertions(+), 27 deletions(-) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt index bbb8953..3de0297 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt @@ -20,7 +20,6 @@ import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.PorterDuff -import android.graphics.drawable.Drawable import android.os.Build import android.widget.CheckBox import android.widget.EditText @@ -28,13 +27,13 @@ import android.widget.ImageButton import android.widget.ProgressBar import android.widget.RadioButton import android.widget.SeekBar -import android.widget.TextView import androidx.annotation.ColorInt import androidx.annotation.FloatRange import androidx.annotation.IntRange import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.Toolbar import androidx.core.graphics.drawable.DrawableCompat +import androidx.core.view.ViewCompat import com.afollestad.materialdialogs.R import java.util.Random @@ -248,37 +247,16 @@ fun Context.textColorStateList(@ColorInt color: Int): ColorStateList { return ColorStateList(states, colors) } -@SuppressLint("RestrictedApi") +/** + * Note that this does not tint the cursor, as there is no public api to do so. + */ fun EditText.tint(@ColorInt color: Int) { val editTextColorStateList = context.textColorStateList(color) if (this is AppCompatEditText) { - supportBackgroundTintList = editTextColorStateList + ViewCompat.setBackgroundTintList(this, editTextColorStateList) } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { backgroundTintList = editTextColorStateList } - tintCursor(color) -} - -fun EditText.tintCursor(@ColorInt color: Int) { - try { - val fCursorDrawableRes = TextView::class.java.getDeclaredField("mCursorDrawableRes") - fCursorDrawableRes.isAccessible = true - val mCursorDrawableRes = fCursorDrawableRes.getInt(this) - val fEditor = TextView::class.java.getDeclaredField("mEditor") - fEditor.isAccessible = true - val editor = fEditor.get(this) - val clazz = editor.javaClass - val fCursorDrawable = clazz.getDeclaredField("mCursorDrawable") - fCursorDrawable.isAccessible = true - val drawables: Array = Array(2, { - val drawable = context.drawable(mCursorDrawableRes) - drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN) - drawable - }) - fCursorDrawable.set(editor, drawables) - } catch (e: Exception) { - e.printStackTrace() - } } fun Toolbar.tint(@ColorInt color: Int, tintTitle: Boolean = true) { -- cgit v1.2.3 From c84edb5a6656cf3fcd10dfb05727b0f3bad5a37d Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 20:28:37 -0700 Subject: Update changelog --- docs/Changelog.md | 1 + sample/src/main/res/xml/kau_changelog.xml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/Changelog.md b/docs/Changelog.md index 689fbd9..795b860 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -4,6 +4,7 @@ * Update Android SDK to 29 and Kotlin to 1.3.31 * :core: Update Material Dialogs to 3.x * :core: Change ProgressAnimator API +* :core: Remove cursor tinting in EditText as it used reflection * :colorpicker: Strip down to just the interface; unless you require the accent palette, it may be fine to just use MD's color extension * :gradle-plugin: Convert to kotlin, rework dependencies, and remove extension hooks diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml index b5ec47c..d6e3d99 100644 --- a/sample/src/main/res/xml/kau_changelog.xml +++ b/sample/src/main/res/xml/kau_changelog.xml @@ -10,10 +10,10 @@ + - -- cgit v1.2.3 From 263ecd778c6f65cf966f9d61e79c4df409a64ede Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 20:35:22 -0700 Subject: Update readme --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 36766b1..04f3c96 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ This library contains small helper functions used throughout almost all of my ot Get it on Google Play -[Changelog](docs/Changelog.md) +[Changelog](docs/Changelog.md) • [Migration](docs/Migration.md) ------------ @@ -102,8 +102,7 @@ This means that you'll need to explicitly include each submodule you'd like to u ## [Color Picker](colorpicker#readme) * Implementation of a color picker dialog with subtle transitions and a decoupled callback -* Includes `:core`, -([`Material Dialogs (commons)`](https://github.com/afollestad/material-dialogs)) +* Includes `:core` ## [KPref Activity](kpref-activity#readme) * Fully programmatic implementation of a Preference Activity, backed by a RecyclerView -- cgit v1.2.3 From 0391c64984d9e0ddbc0f8e0d85e3db8d9085d591 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 20:41:47 -0700 Subject: Update crowdin credits --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 04f3c96..0059e47 100644 --- a/README.md +++ b/README.md @@ -153,9 +153,12 @@ Special thanks to the following awesome people for translating significant porti |----------|--------------| | Chinese (Simplified) | [Alcatelia](https://crowdin.com/profile/Alcatelia) | | Chinese (Traditional) | [yipinghuang](https://crowdin.com/profile/yipinghuang) • [Su, Jun-Ming](https://crowdin.com/profile/sujunmin) • [Wei](https://crowdin.com/profile/wei4green) | +| Danish | [mhtorp](https://crowdin.com/profile/mhtorp) | +| Dutch | [ItGuillaume](https://crowdin.com/profile/ltGuillaume) • [Tatum ter Kuile](https://crowdin.com/profile/Teitr) | | French | [Vincent Kulak](https://github.com/VonOx) • [Jean-Philippe Gravel](https://crowdin.com/profile/wokija) | | Galician | [Xesús M. Mosquera](https://twitter.com/xesusmmc?lang=en) | | German | [Bushido1992](https://forum.xda-developers.com/member.php?u=5179246) • [Marcel Soehnchen](https://crowdin.com/profile/msoehnchen) • [3LD0mi HA](https://forum.xda-developers.com/member.php?u=5860523) | +| Hungarian | [János Erkli](https://crowdin.com/profile/erklijani0521) | | Indonesian | [M. Angga Ariska](https://www.youtube.com/channel/UCkqMw81s2aw7bYO-U2YhD7w) | | Italian | [Bonnee](https://github.com/Bonnee) | | Korean | [잇스테이크](https://crowdin.com/profile/bexco2010) | @@ -165,8 +168,10 @@ Special thanks to the following awesome people for translating significant porti | Portuguese (Brazilian) | [TheusKhan](https://crowdin.com/profile/TheusKhan) | | Serbian | [Nikola Radmanović](https://crowdin.com/profile/nikoladradmanovic) | | Spanish | [Jahir Fiquitiva](https://jahirfiquitiva.me/) • [Nefi Salazar](https://plus.google.com/u/0/105547968033551087431)| +| Swedish | [Artswitcher](https://crowdin.com/profile/Artswitcher) | | Thai | [Thanawat Hanthong](https://crowdin.com/profile/peet6015) | | Turkish | [zuma17](https://crowdin.com/profile/zuma17) | +| Ukrainian | [Вадим Жушман](https://crowdin.com/profile/android54544) | | Vietnamese | [Alienz](https://crowdin.com/profile/alienyd) | The full activity stream for the translations can be found [here](https://crowdin.com/project/kotlin-android-utils/activity_stream) -- cgit v1.2.3 From 8725ae5647e15a38281ca6778b33413490dff91b Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Thu, 13 Jun 2019 21:36:04 -0700 Subject: Translations (#207) * New translations strings_about.xml (Arabic) * New translations strings_colorpicker.xml (Arabic) * New translations strings_commons.xml (Arabic) * New translations strings_mediapicker.xml (Arabic) * New translations strings_about.xml (Afrikaans) * New translations strings_colorpicker.xml (Russian) * New translations strings_about.xml (Russian) * New translations strings_mediapicker.xml (Romanian) * New translations strings_commons.xml (Romanian) * New translations strings_colorpicker.xml (Romanian) * New translations strings_about.xml (Romanian) * New translations strings_mediapicker.xml (Russian) * New translations strings_mediapicker.xml (Japanese) * New translations strings_commons.xml (Japanese) * New translations strings_colorpicker.xml (Japanese) * New translations strings_commons.xml (Russian) * New translations strings_mediapicker.xml (Tagalog) * New translations strings_commons.xml (Tagalog) * New translations strings_colorpicker.xml (Tagalog) * New translations strings_about.xml (Tagalog) * New translations strings_about.xml (Japanese) * New translations strings_colorpicker.xml (Afrikaans) * New translations strings_mediapicker.xml (Czech) * New translations strings_commons.xml (Czech) * New translations strings_colorpicker.xml (Czech) * New translations strings_about.xml (Czech) * New translations strings_about.xml (Finnish) * New translations strings_mediapicker.xml (Catalan) * New translations strings_commons.xml (Catalan) * New translations strings_colorpicker.xml (Catalan) * New translations strings_about.xml (Catalan) * New translations strings_mediapicker.xml (Afrikaans) * New translations strings_commons.xml (Afrikaans) * New translations strings_colorpicker.xml (Finnish) * New translations strings_commons.xml (Greek) * New translations strings_mediapicker.xml (Hebrew) * New translations strings_commons.xml (Hebrew) * New translations strings_colorpicker.xml (Hebrew) * New translations strings_about.xml (Hebrew) * New translations strings_mediapicker.xml (Greek) * New translations strings_colorpicker.xml (Greek) * New translations strings_commons.xml (Finnish) * New translations strings_about.xml (Greek) * New translations strings_mediapicker.xml (Finnish) * New translations strings_about.xml (Tamil) * New translations strings_colorpicker.xml (Tamil) * New translations strings_commons.xml (Tamil) * New translations strings_mediapicker.xml (Tamil) * New translations strings_mediapicker.xml (Arabic) * New translations strings_commons.xml (Arabic) * New translations strings_about.xml (Arabic) * Add credits * Delete blank translations * Update changelog --- README.md | 1 + about/src/main/res/values-ar-rSA/strings_about.xml | 6 +++ .../res-public/values-ar-rSA/strings_commons.xml | 55 ++++++++++++++++++++++ docs/Changelog.md | 1 + files/translation_migration.sh | 0 .../main/res/values-ar-rSA/strings_mediapicker.xml | 11 +++++ sample/src/main/res/xml/kau_changelog.xml | 1 + 7 files changed, 75 insertions(+) create mode 100644 about/src/main/res/values-ar-rSA/strings_about.xml create mode 100644 core/src/main/res-public/values-ar-rSA/strings_commons.xml mode change 100644 => 100755 files/translation_migration.sh create mode 100644 mediapicker/src/main/res/values-ar-rSA/strings_mediapicker.xml diff --git a/README.md b/README.md index 0059e47..2c72eb1 100644 --- a/README.md +++ b/README.md @@ -151,6 +151,7 @@ Special thanks to the following awesome people for translating significant porti | Language | Contributors | |----------|--------------| +| Arabic | [Mohammed Qubati](https://crowdin.com/profile/Mrkqubati) | | Chinese (Simplified) | [Alcatelia](https://crowdin.com/profile/Alcatelia) | | Chinese (Traditional) | [yipinghuang](https://crowdin.com/profile/yipinghuang) • [Su, Jun-Ming](https://crowdin.com/profile/sujunmin) • [Wei](https://crowdin.com/profile/wei4green) | | Danish | [mhtorp](https://crowdin.com/profile/mhtorp) | diff --git a/about/src/main/res/values-ar-rSA/strings_about.xml b/about/src/main/res/values-ar-rSA/strings_about.xml new file mode 100644 index 0000000..f80ee4d --- /dev/null +++ b/about/src/main/res/values-ar-rSA/strings_about.xml @@ -0,0 +1,6 @@ + + + + لن يتم تمكين هذا التطبيق بدون المكتبات الكبيرة التالية. + الأسئلة الشائعة + diff --git a/core/src/main/res-public/values-ar-rSA/strings_commons.xml b/core/src/main/res-public/values-ar-rSA/strings_commons.xml new file mode 100644 index 0000000..efaa623 --- /dev/null +++ b/core/src/main/res-public/values-ar-rSA/strings_commons.xml @@ -0,0 +1,55 @@ + + + + + عن التطبيق + حول %s + إضافة حساب + العودة + إلغاء + سجل التغييرات + إغلاق + التواصل معنا + نسخ + مخصص + مظلم + الافتراضي + لا تظهر مجدداً + تم + خطأ + خروج + هل أنت متأكد بأنك تريد الخروج؟ + هل أنت متأكد بأنك تريد الخروج %s؟ + زجاجي + فهمت + رائع + إخفاء + فاتح + تسجيل الدخول + تسجيل الخروج + هل أنت متأكد بأنك تريد تسجيل الخروج %s؟ + إدارة الحساب + ربما + القائمة + لا + لم يتم العثور على نتائج + لا يوجد + حسناً + Google play + تقييم + الإبلاغ عن خطأ + البحث + إرسال ملاحظات + الإرسال عبر + الاعدادات + المشاركة + تم نسخ النص إلى الحافظة. + شكراً + أوبس + تحذير + نعم + الإذن مرفوض + diff --git a/docs/Changelog.md b/docs/Changelog.md index 795b860..c68629f 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -2,6 +2,7 @@ ## v5.0.0 * Update Android SDK to 29 and Kotlin to 1.3.31 +* Update translations * :core: Update Material Dialogs to 3.x * :core: Change ProgressAnimator API * :core: Remove cursor tinting in EditText as it used reflection diff --git a/files/translation_migration.sh b/files/translation_migration.sh old mode 100644 new mode 100755 diff --git a/mediapicker/src/main/res/values-ar-rSA/strings_mediapicker.xml b/mediapicker/src/main/res/values-ar-rSA/strings_mediapicker.xml new file mode 100644 index 0000000..c05d161 --- /dev/null +++ b/mediapicker/src/main/res/values-ar-rSA/strings_mediapicker.xml @@ -0,0 +1,11 @@ + + + + لم يتم العثور على أي عنصر + لم يتم إختيار أي عنصر + لم يتم تحميل أي عنصر + لا توجد كاميرا + يرجى تثبيت تطبيق كاميرا والمحاولة مجدداً. + فشل في حفظ صورة مؤقتة. + اختيار وسائط + diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml index d6e3d99..d59ce53 100644 --- a/sample/src/main/res/xml/kau_changelog.xml +++ b/sample/src/main/res/xml/kau_changelog.xml @@ -8,6 +8,7 @@ + -- cgit v1.2.3 From 80b303819d18c3c34c4071f352134b3048768805 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 17 Jun 2019 23:35:08 -0700 Subject: Separate adapter from fastadapter --- README.md | 18 +- about/build.gradle | 2 +- adapter/README.md | 16 +- adapter/build.gradle | 3 - adapter/src/main/res/values/dimens.xml | 3 - core-ui/build.gradle | 2 - fastadapter/.gitignore | 1 + fastadapter/README.md | 17 + fastadapter/build.gradle | 12 + fastadapter/progress-proguard.txt | 1 + fastadapter/src/main/AndroidManifest.xml | 1 + .../ca/allanwang/kau/adapters/AdapterUtils.kt | 44 ++ .../kau/adapters/FastItemThemedAdapter.kt | 201 ++++++ .../kau/adapters/RepeatedClickListener.kt | 70 ++ .../allanwang/kau/animators/AnimatorInterfaces.kt | 50 ++ .../allanwang/kau/animators/BaseItemAnimator.java | 765 +++++++++++++++++++++ .../ca/allanwang/kau/animators/DefaultAnimator.kt | 77 +++ .../allanwang/kau/animators/FadeScaleAnimator.kt | 73 ++ .../ca/allanwang/kau/animators/KauAnimator.kt | 89 +++ .../ca/allanwang/kau/animators/NoAnimator.kt | 55 ++ .../ca/allanwang/kau/animators/SlideAnimator.kt | 83 +++ .../kotlin/ca/allanwang/kau/iitems/CardIItem.kt | 140 ++++ .../kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt | 59 ++ .../kotlin/ca/allanwang/kau/iitems/KauIItem.kt | 42 ++ fastadapter/src/main/res/layout/kau_iitem_card.xml | 74 ++ .../src/main/res/layout/kau_iitem_header.xml | 16 + mediapicker/build.gradle | 2 +- sample/build.gradle | 2 +- searchview/build.gradle | 2 +- settings.gradle | 1 + 30 files changed, 1887 insertions(+), 34 deletions(-) delete mode 100644 adapter/src/main/res/values/dimens.xml create mode 100644 fastadapter/.gitignore create mode 100644 fastadapter/README.md create mode 100644 fastadapter/build.gradle create mode 100644 fastadapter/progress-proguard.txt create mode 100644 fastadapter/src/main/AndroidManifest.xml create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/AdapterUtils.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/RepeatedClickListener.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/AnimatorInterfaces.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/animators/SlideAnimator.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt create mode 100644 fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt create mode 100644 fastadapter/src/main/res/layout/kau_iitem_card.xml create mode 100644 fastadapter/src/main/res/layout/kau_iitem_header.xml diff --git a/README.md b/README.md index 2c72eb1..ae78c87 100644 --- a/README.md +++ b/README.md @@ -88,25 +88,29 @@ This means that you'll need to explicitly include each submodule you'd like to u ## [Core UI](core-ui#readme) * Collection of complex views and widgets -* Includes `:core`, `:adapter` +* Includes `:core` ## [About](about#readme) * Modularized overlaying about section. Comes with a main panel, automatic lib detection, and a FAQ parser; also includes the lib strings for KAU. -* Includes `:core-ui`, `:adapter`, +* Includes `:core-ui`, `:fastadapter`, [`About Libraries`](https://github.com/mikepenz/AboutLibraries) ## [Adapter](adapter#readme) -* Kotlin bindings for the fast adapter, as well as modularized RecyclerView animations -* Includes `:core`, -[`Fast Adapter`](https://github.com/mikepenz/FastAdapter) +* RecyclerView animations +* Includes `:core` ## [Color Picker](colorpicker#readme) * Implementation of a color picker dialog with subtle transitions and a decoupled callback * Includes `:core` +## [FastAdapter](fastadapter#readme) +* Kotlin bindings for the fast adapter +* Includes `:core`, `:adapter`, +[`Fast Adapter`](https://github.com/mikepenz/FastAdapter) + ## [KPref Activity](kpref-activity#readme) * Fully programmatic implementation of a Preference Activity, backed by a RecyclerView -* Includes `:core`, `:adapter`, `:colorpicker` +* Includes `:core`, `:fastadapter`, `:colorpicker` ## [Media Picker](mediapicker#readme) * Fully functional image and video pickers, both as an overlay and as a requested activity. @@ -116,7 +120,7 @@ This means that you'll need to explicitly include each submodule you'd like to u ## [SearchView](searchview#readme) * Material searchview with kotlin bindings -* Includes `:core-ui`, `:adapter` +* Includes `:core-ui`, `:fastadapter` ## [Gradle Plugin](buildSrc#readme) * Gradle plugin to help facilitate versioning and other gradle functions diff --git a/about/build.gradle b/about/build.gradle index 5a2a395..b970530 100644 --- a/about/build.gradle +++ b/about/build.gradle @@ -8,7 +8,7 @@ android { dependencies { implementation project(':core-ui') - implementation project(':adapter') + implementation project(':fastadapter') api kau.Dependencies.aboutLibraries } diff --git a/adapter/README.md b/adapter/README.md index a6ffb6d..f3beaff 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -1,20 +1,6 @@ # KAU :adapter -Collection of kotlin bindings and custom IItems for [Fast Adapter](https://github.com/mikepenz/FastAdapter) - -## KauIItems - -Abstract base that extends `AbstractIItems` and contains the arguments `(layoutRes, ViewHolder lambda, idRes)` in that order. -Those variables are used to override the default abstract functions. -If a layout is only used for one item, it may also be used as the id, which you may leave blank in this case. -The ViewHolder lambda is typically of the form `::ViewHolder` -Where you will have a nested class `ViewHolder(v: View) : RecyclerView.ViewHolder(v)` - -## IItem Templates - -* CardIItem - generic all encompassing card item with a title, description, imageview, and button. -All items except for the title are optional. -* HeaderIItem - simple title container with a big top margin +Helpers dealing with recyclerviews and adapters ## KauAnimator diff --git a/adapter/build.gradle b/adapter/build.gradle index de42b6c..42c6eba 100644 --- a/adapter/build.gradle +++ b/adapter/build.gradle @@ -4,9 +4,6 @@ apply from: '../android-lib.gradle' dependencies { implementation project(':core') - - api kau.Dependencies.fastAdapter - api kau.Dependencies.fastAdapterCommons } apply from: '../artifacts.gradle' diff --git a/adapter/src/main/res/values/dimens.xml b/adapter/src/main/res/values/dimens.xml deleted file mode 100644 index 193940e..0000000 --- a/adapter/src/main/res/values/dimens.xml +++ /dev/null @@ -1,3 +0,0 @@ - - 56dp - diff --git a/core-ui/build.gradle b/core-ui/build.gradle index c2732c6..23d601d 100644 --- a/core-ui/build.gradle +++ b/core-ui/build.gradle @@ -4,8 +4,6 @@ apply from: '../android-lib.gradle' dependencies { api project(':core') - implementation project(':adapter') - } apply from: '../artifacts.gradle' diff --git a/fastadapter/.gitignore b/fastadapter/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/fastadapter/.gitignore @@ -0,0 +1 @@ +/build diff --git a/fastadapter/README.md b/fastadapter/README.md new file mode 100644 index 0000000..fa339f7 --- /dev/null +++ b/fastadapter/README.md @@ -0,0 +1,17 @@ +# KAU :fastadapter + +Collection of kotlin bindings and custom IItems for [Fast Adapter](https://github.com/mikepenz/FastAdapter) + +## KauIItems + +Abstract base that extends `AbstractIItems` and contains the arguments `(layoutRes, ViewHolder lambda, idRes)` in that order. +Those variables are used to override the default abstract functions. +If a layout is only used for one item, it may also be used as the id, which you may leave blank in this case. +The ViewHolder lambda is typically of the form `::ViewHolder` +Where you will have a nested class `ViewHolder(v: View) : RecyclerView.ViewHolder(v)` + +## IItem Templates + +* CardIItem - generic all encompassing card item with a title, description, imageview, and button. +All items except for the title are optional. +* HeaderIItem - simple title container with a big top margin \ No newline at end of file diff --git a/fastadapter/build.gradle b/fastadapter/build.gradle new file mode 100644 index 0000000..86fd084 --- /dev/null +++ b/fastadapter/build.gradle @@ -0,0 +1,12 @@ +ext.kauSubModuleMinSdk = kau.Versions.coreMinSdk + +apply from: '../android-lib.gradle' + +dependencies { + api project(':adapter') + + api kau.Dependencies.fastAdapter + api kau.Dependencies.fastAdapterCommons +} + +apply from: '../artifacts.gradle' diff --git a/fastadapter/progress-proguard.txt b/fastadapter/progress-proguard.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/fastadapter/progress-proguard.txt @@ -0,0 +1 @@ + diff --git a/fastadapter/src/main/AndroidManifest.xml b/fastadapter/src/main/AndroidManifest.xml new file mode 100644 index 0000000..ab8cebb --- /dev/null +++ b/fastadapter/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/AdapterUtils.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/AdapterUtils.kt new file mode 100644 index 0000000..17fd09f --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/AdapterUtils.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.adapters + +import com.mikepenz.fastadapter.FastAdapter +import com.mikepenz.fastadapter.IAdapter +import com.mikepenz.fastadapter.IAdapterExtension +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.select.SelectExtension + +/** + * Created by Allan Wang on 2017-11-08. + */ + +/** + * Add kotlin's generic syntax to better support out types + */ +fun > fastAdapter(vararg adapter: IAdapter) = + FastAdapter.with>(adapter.toList())!! + +inline fun , Item : IItem<*, *>> FastAdapter.getExtension(): T? = + getExtension(T::class.java) + +/** + * Returns selection size, or -1 if selection is disabled + */ +inline val > IAdapter.selectionSize: Int + get() = fastAdapter.getExtension, Item>()?.selections?.size ?: -1 + +inline val > IAdapter.selectedItems: Set + get() = fastAdapter.getExtension, Item>()?.selectedItems ?: emptySet() diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt new file mode 100644 index 0000000..152982f --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/FastItemThemedAdapter.kt @@ -0,0 +1,201 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.adapters + +import android.content.res.ColorStateList +import android.os.Build +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.annotation.RequiresApi +import ca.allanwang.kau.ui.createSimpleRippleDrawable +import ca.allanwang.kau.utils.adjustAlpha +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter + +/** + * Created by Allan Wang on 2017-06-29. + * + * Adapter with a set of colors that will be added to all subsequent items + * Changing a color while the adapter is not empty will reload all items + * + * This adapter overrides every method where an item is added + * If that item extends [ThemableIItem], then the colors will be set + */ +class FastItemThemedAdapter>( + textColor: Int? = null, + backgroundColor: Int? = null, + accentColor: Int? = null +) : FastItemAdapter() { + constructor(colors: ThemableIItemColors) : this(colors.textColor, colors.backgroundColor, colors.accentColor) + + var textColor: Int? = textColor + set(value) { + if (field == value) return + field = value + themeChanged() + } + var backgroundColor: Int? = backgroundColor + set(value) { + if (field == value) return + field = value + themeChanged() + } + var accentColor: Int? = accentColor + set(value) { + if (field == value) return + field = value + themeChanged() + } + + fun setColors(colors: ThemableIItemColors) { + this.textColor = colors.textColor + this.backgroundColor = colors.backgroundColor + this.accentColor = colors.accentColor + } + + fun themeChanged() { + if (adapterItemCount == 0) return + injectTheme(adapterItems) + notifyAdapterDataSetChanged() + } + + override fun add(position: Int, items: List): FastItemAdapter { + injectTheme(items) + return super.add(position, items) + } + + override fun add(position: Int, item: Item): FastItemAdapter { + injectTheme(item) + return super.add(position, item) + } + + override fun add(item: Item): FastItemAdapter { + injectTheme(item) + return super.add(item) + } + + override fun add(items: List?): FastItemAdapter { + injectTheme(items) + return super.add(items) + } + + override fun set(items: List?): FastItemAdapter { + injectTheme(items) + return super.set(items) + } + + override fun set(position: Int, item: Item): FastItemAdapter { + injectTheme(item) + return super.set(position, item) + } + + override fun setNewList(items: List?, retainFilter: Boolean): FastItemAdapter { + injectTheme(items) + return super.setNewList(items, retainFilter) + } + + override fun setNewList(items: List?): FastItemAdapter { + injectTheme(items) + return super.setNewList(items) + } + + private fun injectTheme(items: Collection?>?) { + items?.forEach { injectTheme(it) } + } + + protected fun injectTheme(item: IItem<*, *>?) { + if (item is ThemableIItem && item.themeEnabled) { + item.textColor = textColor + item.backgroundColor = backgroundColor + item.accentColor = accentColor + } + } +} + +interface ThemableIItemColors { + var textColor: Int? + var backgroundColor: Int? + var accentColor: Int? +} + +class ThemableIItemColorsDelegate : ThemableIItemColors { + override var textColor: Int? = null + override var backgroundColor: Int? = null + override var accentColor: Int? = null +} + +/** + * Interface that needs to be implemented by every iitem + * Holds the color values and has helper methods to inject the colors + */ +interface ThemableIItem : ThemableIItemColors { + var themeEnabled: Boolean + fun bindTextColor(vararg views: TextView?) + fun bindTextColorSecondary(vararg views: TextView?) + fun bindDividerColor(vararg views: View?) + fun bindAccentColor(vararg views: TextView?) + fun bindBackgroundColor(vararg views: View?) + fun bindBackgroundRipple(vararg views: View?) + fun bindIconColor(vararg views: ImageView?) +} + +/** + * The delegate for [ThemableIItem] + */ +class ThemableIItemDelegate : ThemableIItem, ThemableIItemColors by ThemableIItemColorsDelegate() { + override var themeEnabled: Boolean = true + + override fun bindTextColor(vararg views: TextView?) { + val color = textColor ?: return + views.forEach { it?.setTextColor(color) } + } + + override fun bindTextColorSecondary(vararg views: TextView?) { + val color = textColor?.adjustAlpha(0.8f) ?: return + views.forEach { it?.setTextColor(color) } + } + + override fun bindAccentColor(vararg views: TextView?) { + val color = accentColor ?: textColor ?: return + views.forEach { it?.setTextColor(color) } + } + + override fun bindDividerColor(vararg views: View?) { + val color = (textColor ?: accentColor)?.adjustAlpha(0.1f) ?: return + views.forEach { it?.setBackgroundColor(color) } + } + + override fun bindBackgroundColor(vararg views: View?) { + val color = backgroundColor ?: return + views.forEach { it?.setBackgroundColor(color) } + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + override fun bindBackgroundRipple(vararg views: View?) { + val background = backgroundColor ?: return + val foreground = accentColor ?: textColor ?: backgroundColor + ?: return //default to normal background + val ripple = createSimpleRippleDrawable(foreground, background) + views.forEach { it?.background = ripple } + } + + @RequiresApi(Build.VERSION_CODES.LOLLIPOP) + override fun bindIconColor(vararg views: ImageView?) { + val color = accentColor ?: textColor ?: return + views.forEach { it?.drawable?.setTintList(ColorStateList.valueOf(color)) } + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/RepeatedClickListener.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/RepeatedClickListener.kt new file mode 100644 index 0000000..40b4774 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/adapters/RepeatedClickListener.kt @@ -0,0 +1,70 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.adapters + +import android.view.View +import androidx.annotation.IntRange +import com.mikepenz.fastadapter.FastAdapter +import com.mikepenz.fastadapter.IAdapter +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.listeners.OnClickListener + +/** + * Created by Allan Wang on 26/12/17. + */ +fun > FastAdapter.withOnRepeatedClickListener( + count: Int, + duration: Long, + event: OnClickListener +) = + withOnClickListener(RepeatedClickListener(count, duration, event)) + +/** + * Registers and skips each click until the designated [count] clicks are triggered, + * each within [duration] from each other. + * Only then will the [event] be fired, and everything will be reset. + */ +private class RepeatedClickListener>( + @IntRange(from = 1) val count: Int, + @IntRange(from = 1) val duration: Long, + val event: OnClickListener +) : OnClickListener { + + init { + if (count <= 0) + throw IllegalArgumentException("RepeatedClickListener's count must be > 1") + if (duration <= 0) + throw IllegalArgumentException("RepeatedClickListener's duration must be > 1L") + } + + private var chain = 0 + private var time = -1L + + override fun onClick(v: View?, adapter: IAdapter, item: Item, position: Int): Boolean { + val now = System.currentTimeMillis() + if (time - now < duration) + chain++ + else + chain = 1 + time = now + if (chain == count) { + chain = 0 + event.onClick(v, adapter, item, position) + return true + } + return false + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/AnimatorInterfaces.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/AnimatorInterfaces.kt new file mode 100644 index 0000000..b83a2d0 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/AnimatorInterfaces.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.View +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView + +/** + * Created by Allan Wang on 2017-07-11. + */ +class KauAnimatorException(message: String) : RuntimeException(message) + +interface KauAnimatorAdd { + fun animationPrepare(holder: RecyclerView.ViewHolder): View.() -> Unit + fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit + fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit + fun getDelay(remove: Long, move: Long, change: Long): Long + var itemDelayFactor: Float +} + +interface KauAnimatorRemove { + fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit + fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit + fun getDelay(remove: Long, move: Long, change: Long): Long + var itemDelayFactor: Float +} + +interface KauAnimatorChange { + fun changeOldAnimation( + holder: RecyclerView.ViewHolder, + changeInfo: BaseItemAnimator.ChangeInfo + ): ViewPropertyAnimator.() -> Unit + + fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit + fun changeAnimationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java new file mode 100644 index 0000000..4ead735 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java @@ -0,0 +1,765 @@ +package ca.allanwang.kau.animators; + +/* + * Created by Allan Wang on 2017-06-27. + * + * Based on Item Animator by {@author Mike Penz} + * Rewritten to match with the updated compat dependencies + */ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.TimeInterpolator; +import android.animation.ValueAnimator; +import androidx.annotation.NonNull; +import androidx.core.view.ViewCompat; +import androidx.recyclerview.widget.RecyclerView.ViewHolder; +import androidx.recyclerview.widget.SimpleItemAnimator; + +import android.view.View; +import android.view.ViewPropertyAnimator; +import android.view.animation.Interpolator; + +import java.util.ArrayList; +import java.util.List; + +/** + * This implementation of {@link androidx.recyclerview.widget.RecyclerView.ItemAnimator} provides basic + * animations on remove, add, and move events that happen to the items in + * a RecyclerView. RecyclerView uses a DefaultItemAnimator by default. + * + * @see androidx.recyclerview.widget.RecyclerView#setItemAnimator(androidx.recyclerview.widget.RecyclerView.ItemAnimator) + */ +public abstract class BaseItemAnimator extends SimpleItemAnimator { + private static final boolean DEBUG = false; + + private static TimeInterpolator sDefaultInterpolator; + + private ArrayList mPendingRemovals = new ArrayList<>(); + private ArrayList mPendingAdditions = new ArrayList<>(); + private ArrayList mPendingMoves = new ArrayList<>(); + private ArrayList mPendingChanges = new ArrayList<>(); + + ArrayList> mAdditionsList = new ArrayList<>(); + ArrayList> mMovesList = new ArrayList<>(); + ArrayList> mChangesList = new ArrayList<>(); + + ArrayList mAddAnimations = new ArrayList<>(); + ArrayList mMoveAnimations = new ArrayList<>(); + ArrayList mRemoveAnimations = new ArrayList<>(); + ArrayList mChangeAnimations = new ArrayList<>(); + + public Interpolator interpolator; + + private static class MoveInfo { + public ViewHolder holder; + public int fromX, fromY, toX, toY; + + MoveInfo(ViewHolder holder, int fromX, int fromY, int toX, int toY) { + this.holder = holder; + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + } + + public static class ChangeInfo { + public ViewHolder oldHolder, newHolder; + public int fromX, fromY, toX, toY; + + private ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder) { + this.oldHolder = oldHolder; + this.newHolder = newHolder; + } + + ChangeInfo(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + this(oldHolder, newHolder); + this.fromX = fromX; + this.fromY = fromY; + this.toX = toX; + this.toY = toY; + } + + @Override + public String toString() { + return "ChangeInfo{" + + "oldHolder=" + oldHolder + + ", newHolder=" + newHolder + + ", fromX=" + fromX + + ", fromY=" + fromY + + ", toX=" + toX + + ", toY=" + toY + + '}'; + } + } + + @Override + public void runPendingAnimations() { + boolean removalsPending = !mPendingRemovals.isEmpty(); + boolean movesPending = !mPendingMoves.isEmpty(); + boolean changesPending = !mPendingChanges.isEmpty(); + boolean additionsPending = !mPendingAdditions.isEmpty(); + if (!removalsPending && !movesPending && !additionsPending && !changesPending) { + // nothing to animate + return; + } + // First, remove stuff + for (ViewHolder holder : mPendingRemovals) { + animateRemoveImpl(holder); + } + mPendingRemovals.clear(); + // Next, move stuff + if (movesPending) { + final ArrayList moves = new ArrayList<>(); + moves.addAll(mPendingMoves); + mMovesList.add(moves); + mPendingMoves.clear(); + Runnable mover = new Runnable() { + @Override + public void run() { + for (MoveInfo moveInfo : moves) { + animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, + moveInfo.toX, moveInfo.toY); + } + moves.clear(); + mMovesList.remove(moves); + } + }; + if (removalsPending) { + View view = moves.get(0).holder.itemView; + ViewCompat.postOnAnimationDelayed(view, mover, 0); + } else { + mover.run(); + } + } + // Next, change stuff, to run in parallel with move animations + if (changesPending) { + final ArrayList changes = new ArrayList<>(); + changes.addAll(mPendingChanges); + mChangesList.add(changes); + mPendingChanges.clear(); + Runnable changer = new Runnable() { + @Override + public void run() { + for (ChangeInfo change : changes) { + animateChangeImpl(change); + } + changes.clear(); + mChangesList.remove(changes); + } + }; + if (removalsPending) { + ViewHolder holder = changes.get(0).oldHolder; + long moveDuration = movesPending ? getMoveDuration() : 0; + ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDelay(getRemoveDuration(), moveDuration, getChangeDuration())); + } else { + changer.run(); + } + } + // Next, add stuff + if (additionsPending) { + final ArrayList additions = new ArrayList<>(); + additions.addAll(mPendingAdditions); + mAdditionsList.add(additions); + mPendingAdditions.clear(); + Runnable adder = new Runnable() { + @Override + public void run() { + for (ViewHolder holder : additions) { + animateAddImpl(holder); + } + additions.clear(); + mAdditionsList.remove(additions); + } + }; + if (removalsPending || movesPending || changesPending) { + long removeDuration = removalsPending ? getRemoveDuration() : 0; + long moveDuration = movesPending ? getMoveDuration() : 0; + long changeDuration = changesPending ? getChangeDuration() : 0; + View view = additions.get(0).itemView; + ViewCompat.postOnAnimationDelayed(view, adder, getAddDelay(removeDuration, moveDuration, changeDuration)); + } else { + adder.run(); + } + } + } + + /** + * used to calculated the delay until the remove animation should start + * + * @param remove the remove duration + * @param move the move duration + * @param change the change duration + * @return the calculated delay for the remove items animation + */ + public long getRemoveDelay(long remove, long move, long change) { + return remove + Math.max(move, change); + } + + /** + * used to calculated the delay until the add animation should start + * + * @param remove the remove duration + * @param move the move duration + * @param change the change duration + * @return the calculated delay for the add items animation + */ + public long getAddDelay(long remove, long move, long change) { + return remove + Math.max(move, change); + } + + @Override + public boolean animateRemove(final ViewHolder holder) { + resetAnimation(holder); + mPendingRemovals.add(holder); + return true; + } + + private void animateRemoveImpl(final ViewHolder holder) { + final ViewPropertyAnimator animation = removeAnimation(holder); + mRemoveAnimations.add(holder); + animation.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchRemoveStarting(holder); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + removeAnimationCleanup(holder); + dispatchRemoveFinished(holder); + mRemoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + abstract public ViewPropertyAnimator removeAnimation(ViewHolder holder); + + abstract public void removeAnimationCleanup(ViewHolder holder); + + @Override + public boolean animateAdd(final ViewHolder holder) { + resetAnimation(holder); + addAnimationPrepare(holder); + mPendingAdditions.add(holder); + return true; + } + + void animateAddImpl(final ViewHolder holder) { + final ViewPropertyAnimator animation = addAnimation(holder); + mAddAnimations.add(holder); + animation.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchAddStarting(holder); + } + + @Override + public void onAnimationCancel(Animator animator) { + addAnimationCleanup(holder); + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchAddFinished(holder); + mAddAnimations.remove(holder); + dispatchFinishedWhenDone(); + addAnimationCleanup(holder); + } + }).start(); + } + + /** + * the animation to prepare the view before the add animation is run + * + * @param holder + */ + public abstract void addAnimationPrepare(ViewHolder holder); + + /** + * the animation for adding a view + * + * @param holder + * @return + */ + public abstract ViewPropertyAnimator addAnimation(ViewHolder holder); + + /** + * the cleanup method if the animation needs to be stopped. and tro prepare for the next view + * + * @param holder + */ + abstract void addAnimationCleanup(ViewHolder holder); + + @Override + public boolean animateMove(final ViewHolder holder, int fromX, int fromY, + int toX, int toY) { + final View view = holder.itemView; + fromX += (int) holder.itemView.getTranslationX(); + fromY += (int) holder.itemView.getTranslationY(); + resetAnimation(holder); + int deltaX = toX - fromX; + int deltaY = toY - fromY; + if (deltaX == 0 && deltaY == 0) { + dispatchMoveFinished(holder); + return false; + } + if (deltaX != 0) { + view.setTranslationX(-deltaX); + } + if (deltaY != 0) { + view.setTranslationY(-deltaY); + } + mPendingMoves.add(new MoveInfo(holder, fromX, fromY, toX, toY)); + return true; + } + + void animateMoveImpl(final ViewHolder holder, int fromX, int fromY, int toX, int toY) { + final View view = holder.itemView; + final int deltaX = toX - fromX; + final int deltaY = toY - fromY; + if (deltaX != 0) { + view.animate().translationX(0); + } + if (deltaY != 0) { + view.animate().translationY(0); + } + // TODO: make EndActions end listeners instead, since end actions aren't called when + // vpas are canceled (and can't end them. why?) + // need listener functionality in VPACompat for this. Ick. + final ViewPropertyAnimator animation = view.animate(); + mMoveAnimations.add(holder); + animation.setDuration(getMoveDuration()).setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchMoveStarting(holder); + } + + @Override + public void onAnimationCancel(Animator animator) { + if (deltaX != 0) { + view.setTranslationX(0); + } + if (deltaY != 0) { + view.setTranslationY(0); + } + } + + @Override + public void onAnimationEnd(Animator animator) { + animation.setListener(null); + dispatchMoveFinished(holder); + mMoveAnimations.remove(holder); + dispatchFinishedWhenDone(); + } + }).start(); + } + + @Override + public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, + int fromX, int fromY, int toX, int toY) { + if (oldHolder == newHolder) { + // Don't know how to run change animations when the same view holder is re-used. + // run a move animation to handle position changes. + return animateMove(oldHolder, fromX, fromY, toX, toY); + } + changeAnimation(oldHolder, newHolder, + fromX, fromY, toX, toY); + mPendingChanges.add(new ChangeInfo(oldHolder, newHolder, fromX, fromY, toX, toY)); + return true; + } + + void animateChangeImpl(final ChangeInfo changeInfo) { + final ViewHolder holder = changeInfo.oldHolder; + final View view = holder == null ? null : holder.itemView; + final ViewHolder newHolder = changeInfo.newHolder; + final View newView = newHolder != null ? newHolder.itemView : null; + if (view != null) { + final ViewPropertyAnimator oldViewAnim = changeOldAnimation(holder, changeInfo); + mChangeAnimations.add(changeInfo.oldHolder); + oldViewAnim.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.oldHolder, true); + } + + @Override + public void onAnimationEnd(Animator animator) { + oldViewAnim.setListener(null); + changeAnimationCleanup(holder); + view.setTranslationX(0); + view.setTranslationY(0); + dispatchChangeFinished(changeInfo.oldHolder, true); + mChangeAnimations.remove(changeInfo.oldHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + if (newView != null) { + final ViewPropertyAnimator newViewAnimation = changeNewAnimation(newHolder); + mChangeAnimations.add(changeInfo.newHolder); + newViewAnimation.setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animator) { + dispatchChangeStarting(changeInfo.newHolder, false); + } + + @Override + public void onAnimationEnd(Animator animator) { + newViewAnimation.setListener(null); + changeAnimationCleanup(newHolder); + newView.setTranslationX(0); + newView.setTranslationY(0); + dispatchChangeFinished(changeInfo.newHolder, false); + mChangeAnimations.remove(changeInfo.newHolder); + dispatchFinishedWhenDone(); + } + }).start(); + } + } + + /** + * the whole change animation if we have to cross animate two views + * + * @param oldHolder + * @param newHolder + * @param fromX + * @param fromY + * @param toX + * @param toY + */ + public void changeAnimation(ViewHolder oldHolder, ViewHolder newHolder, int fromX, int fromY, int toX, int toY) { + final float prevTranslationX = oldHolder.itemView.getTranslationX(); + final float prevTranslationY = oldHolder.itemView.getTranslationY(); + final float prevValue = oldHolder.itemView.getAlpha(); + resetAnimation(oldHolder); + int deltaX = (int) (toX - fromX - prevTranslationX); + int deltaY = (int) (toY - fromY - prevTranslationY); + // recover prev translation state after ending animation + oldHolder.itemView.setTranslationX(prevTranslationX); + oldHolder.itemView.setTranslationY(prevTranslationY); + + oldHolder.itemView.setAlpha(prevValue); + if (newHolder != null) { + // carry over translation values + resetAnimation(newHolder); + newHolder.itemView.setTranslationX(-deltaX); + newHolder.itemView.setTranslationY(-deltaY); + newHolder.itemView.setAlpha(0); + } + } + + /** + * the animation for removing the old view + * + * @param holder + * @return + */ + public abstract ViewPropertyAnimator changeOldAnimation(ViewHolder holder, ChangeInfo changeInfo); + + /** + * the animation for changing the new view + * + * @param holder + * @return + */ + public abstract ViewPropertyAnimator changeNewAnimation(ViewHolder holder); + + /** + * the cleanup method if the animation needs to be stopped. and to prepare for the next view + * + * @param holder + */ + public abstract void changeAnimationCleanup(ViewHolder holder); + + private void endChangeAnimation(List infoList, ViewHolder item) { + for (int i = infoList.size() - 1; i >= 0; i--) { + ChangeInfo changeInfo = infoList.get(i); + if (endChangeAnimationIfNecessary(changeInfo, item)) { + if (changeInfo.oldHolder == null && changeInfo.newHolder == null) { + infoList.remove(changeInfo); + } + } + } + } + + private void endChangeAnimationIfNecessary(ChangeInfo changeInfo) { + if (changeInfo.oldHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.oldHolder); + } + if (changeInfo.newHolder != null) { + endChangeAnimationIfNecessary(changeInfo, changeInfo.newHolder); + } + } + + private boolean endChangeAnimationIfNecessary(ChangeInfo changeInfo, ViewHolder item) { + boolean oldItem = false; + if (changeInfo.newHolder == item) { + changeInfo.newHolder = null; + } else if (changeInfo.oldHolder == item) { + changeInfo.oldHolder = null; + oldItem = true; + } else { + return false; + } + changeAnimationCleanup(item); + item.itemView.setTranslationX(0); + item.itemView.setTranslationY(0); + dispatchChangeFinished(item, oldItem); + return true; + } + + @Override + public void endAnimation(ViewHolder item) { + final View view = item.itemView; + // this will trigger end callback which should set properties to their target values. + view.animate().cancel(); + // TODO if some other animations are chained to end, how do we cancel them as well? + for (int i = mPendingMoves.size() - 1; i >= 0; i--) { + MoveInfo moveInfo = mPendingMoves.get(i); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + mPendingMoves.remove(i); + } + } + endChangeAnimation(mPendingChanges, item); + if (mPendingRemovals.remove(item)) { + removeAnimationCleanup(item); + dispatchRemoveFinished(item); + } + if (mPendingAdditions.remove(item)) { + addAnimationCleanup(item); + dispatchAddFinished(item); + } + + for (int i = mChangesList.size() - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + endChangeAnimation(changes, item); + if (changes.isEmpty()) { + mChangesList.remove(i); + } + } + for (int i = mMovesList.size() - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + for (int j = moves.size() - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + if (moveInfo.holder == item) { + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(i); + } + break; + } + } + } + for (int i = mAdditionsList.size() - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + if (additions.remove(item)) { + addAnimationCleanup(item); + dispatchAddFinished(item); + if (additions.isEmpty()) { + mAdditionsList.remove(i); + } + } + } + + // animations should be ended by the cancel above. + //noinspection PointlessBooleanExpression,ConstantConditions + if (mRemoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mRemoveAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mAddAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mAddAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mChangeAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mChangeAnimations list"); + } + + //noinspection PointlessBooleanExpression,ConstantConditions + if (mMoveAnimations.remove(item) && DEBUG) { + throw new IllegalStateException("after animation is cancelled, item should not be in " + + "mMoveAnimations list"); + } + dispatchFinishedWhenDone(); + } + + private void resetAnimation(ViewHolder holder) { + + if (sDefaultInterpolator == null) { + sDefaultInterpolator = new ValueAnimator().getInterpolator(); + } + holder.itemView.animate().setInterpolator(sDefaultInterpolator); + endAnimation(holder); + } + + @Override + public boolean isRunning() { + return (!mPendingAdditions.isEmpty() + || !mPendingChanges.isEmpty() + || !mPendingMoves.isEmpty() + || !mPendingRemovals.isEmpty() + || !mMoveAnimations.isEmpty() + || !mRemoveAnimations.isEmpty() + || !mAddAnimations.isEmpty() + || !mChangeAnimations.isEmpty() + || !mMovesList.isEmpty() + || !mAdditionsList.isEmpty() + || !mChangesList.isEmpty()); + } + + /** + * Check the state of currently pending and running animations. If there are none + * pending/running, call {@link #dispatchAnimationsFinished()} to notify any + * listeners. + */ + void dispatchFinishedWhenDone() { + if (!isRunning()) { + dispatchAnimationsFinished(); + } + } + + @Override + public void endAnimations() { + int count = mPendingMoves.size(); + for (int i = count - 1; i >= 0; i--) { + MoveInfo item = mPendingMoves.get(i); + View view = item.holder.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(item.holder); + mPendingMoves.remove(i); + } + count = mPendingRemovals.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingRemovals.get(i); + dispatchRemoveFinished(item); + mPendingRemovals.remove(i); + } + count = mPendingAdditions.size(); + for (int i = count - 1; i >= 0; i--) { + ViewHolder item = mPendingAdditions.get(i); + addAnimationCleanup(item); + dispatchAddFinished(item); + mPendingAdditions.remove(i); + } + count = mPendingChanges.size(); + for (int i = count - 1; i >= 0; i--) { + endChangeAnimationIfNecessary(mPendingChanges.get(i)); + } + mPendingChanges.clear(); + if (!isRunning()) { + return; + } + + int listCount = mMovesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList moves = mMovesList.get(i); + count = moves.size(); + for (int j = count - 1; j >= 0; j--) { + MoveInfo moveInfo = moves.get(j); + ViewHolder item = moveInfo.holder; + View view = item.itemView; + view.setTranslationY(0); + view.setTranslationX(0); + dispatchMoveFinished(moveInfo.holder); + moves.remove(j); + if (moves.isEmpty()) { + mMovesList.remove(moves); + } + } + } + listCount = mAdditionsList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList additions = mAdditionsList.get(i); + count = additions.size(); + for (int j = count - 1; j >= 0; j--) { + ViewHolder item = additions.get(j); + View view = item.itemView; + addAnimationCleanup(item); + dispatchAddFinished(item); + additions.remove(j); + if (additions.isEmpty()) { + mAdditionsList.remove(additions); + } + } + } + listCount = mChangesList.size(); + for (int i = listCount - 1; i >= 0; i--) { + ArrayList changes = mChangesList.get(i); + count = changes.size(); + for (int j = count - 1; j >= 0; j--) { + endChangeAnimationIfNecessary(changes.get(j)); + if (changes.isEmpty()) { + mChangesList.remove(changes); + } + } + } + + cancelAll(mRemoveAnimations); + cancelAll(mMoveAnimations); + cancelAll(mAddAnimations); + cancelAll(mChangeAnimations); + + dispatchAnimationsFinished(); + } + + void cancelAll(List viewHolders) { + for (int i = viewHolders.size() - 1; i >= 0; i--) { + viewHolders.get(i).itemView.animate().cancel(); + } + } + + /** + * {@inheritDoc} + *

+ * If the payload list is not empty, DefaultItemAnimator returns true. + * When this is the case: + *

    + *
  • If you override {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, both + * ViewHolder arguments will be the same instance. + *
  • + *
  • + * If you are not overriding {@link #animateChange(ViewHolder, ViewHolder, int, int, int, int)}, + * then DefaultItemAnimator will call {@link #animateMove(ViewHolder, int, int, int, int)} and + * run a move animation instead. + *
  • + *
+ */ + @Override + public boolean canReuseUpdatedViewHolder(@NonNull ViewHolder viewHolder, + @NonNull List payloads) { + return !payloads.isEmpty() || super.canReuseUpdatedViewHolder(viewHolder, payloads); + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt new file mode 100644 index 0000000..4e342ab --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/DefaultAnimator.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView + +/** + * Created by Allan Wang on 2017-06-27. + */ +open class DefaultAnimator : BaseItemAnimator() { + + override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + alpha(0f) + duration = this@DefaultAnimator.removeDuration + interpolator = this@DefaultAnimator.interpolator + } + } + + override fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.alpha = 1f + } + + override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) { + holder.itemView.alpha = 0f + } + + override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + alpha(1f) + duration = this@DefaultAnimator.addDuration + interpolator = this@DefaultAnimator.interpolator + } + } + + override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.alpha = 1f + } + + override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: ChangeInfo): ViewPropertyAnimator { + return holder.itemView.animate().apply { + alpha(0f) + translationX(changeInfo.toX.toFloat() - changeInfo.fromX) + translationY(changeInfo.toY.toFloat() - changeInfo.fromY) + duration = this@DefaultAnimator.changeDuration + interpolator = this@DefaultAnimator.interpolator + } + } + + override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + alpha(1f) + translationX(0f) + translationY(0f) + duration = this@DefaultAnimator.changeDuration + interpolator = this@DefaultAnimator.interpolator + } + } + + override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.alpha = 1f + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt new file mode 100644 index 0000000..9113b0e --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/FadeScaleAnimator.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.View +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView +import ca.allanwang.kau.utils.scaleXY + +/** + * Created by Allan Wang on 2017-07-11. + */ +class FadeScaleAnimatorAdd(val scaleFactor: Float = 1.0f, override var itemDelayFactor: Float = 0.125f) : + KauAnimatorAdd { + + override fun animationPrepare(holder: RecyclerView.ViewHolder): View.() -> Unit = { + scaleXY = scaleFactor + alpha = 0f + } + + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { + scaleXY(1f) + alpha(1f) + } + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { + scaleXY = 1f + alpha = 1f + } + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} + +class FadeScaleAnimatorRemove(val scaleFactor: Float = 1.0f, override var itemDelayFactor: Float = 0.125f) : + KauAnimatorRemove { + + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { + scaleXY(scaleFactor) + alpha(0f) + } + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { + scaleXY = 1f + alpha = 1f + } + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} + +class FadeAnimatorChange : KauAnimatorChange { + + override fun changeOldAnimation( + holder: RecyclerView.ViewHolder, + changeInfo: BaseItemAnimator.ChangeInfo + ): ViewPropertyAnimator.() -> Unit = { alpha(0f) } + + override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { alpha(1f) } + + override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { alpha = 1f } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt new file mode 100644 index 0000000..b9df946 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/KauAnimator.kt @@ -0,0 +1,89 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView +import ca.allanwang.kau.utils.KAU_BOTTOM +import ca.allanwang.kau.utils.KAU_RIGHT + +/** + * Created by Allan Wang on 2017-06-27. + */ +open class KauAnimator( + val addAnimator: KauAnimatorAdd = SlideAnimatorAdd(KAU_BOTTOM), + val removeAnimator: KauAnimatorRemove = SlideAnimatorRemove(KAU_RIGHT), + val changeAnimator: KauAnimatorChange = FadeAnimatorChange() +) : BaseItemAnimator() { + + open fun startDelay(holder: RecyclerView.ViewHolder, duration: Long, factor: Float) = + Math.max(0L, (holder.adapterPosition * duration * factor).toLong()) + + override fun removeAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + startDelay = startDelay(holder, removeDuration, removeAnimator.itemDelayFactor) + duration = removeDuration + interpolator = this@KauAnimator.interpolator + removeAnimator.animation(holder)() + } + } + + override fun removeAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.apply { removeAnimator.animationCleanup(holder)() } + } + + override fun getRemoveDelay(remove: Long, move: Long, change: Long): Long = + removeAnimator.getDelay(remove, move, change) + + override fun addAnimationPrepare(holder: RecyclerView.ViewHolder) { + holder.itemView.apply { addAnimator.animationPrepare(holder)() } + } + + override fun addAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + startDelay = startDelay(holder, addDuration, addAnimator.itemDelayFactor) + duration = addDuration + interpolator = this@KauAnimator.interpolator + addAnimator.animation(holder)() + } + } + + override fun addAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.apply { addAnimator.animationCleanup(holder)() } + } + + override fun getAddDelay(remove: Long, move: Long, change: Long): Long = addAnimator.getDelay(remove, move, change) + + override fun changeOldAnimation(holder: RecyclerView.ViewHolder, changeInfo: ChangeInfo): ViewPropertyAnimator { + return holder.itemView.animate().apply { + duration = changeDuration + interpolator = this@KauAnimator.interpolator + changeAnimator.changeOldAnimation(holder, changeInfo)() + } + } + + override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator { + return holder.itemView.animate().apply { + duration = changeDuration + interpolator = this@KauAnimator.interpolator + changeAnimator.changeNewAnimation(holder)() + } + } + + override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder) { + holder.itemView.apply { changeAnimator.changeAnimationCleanup(holder)() } + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt new file mode 100644 index 0000000..cca8a25 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/NoAnimator.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.View +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView + +/** + * Created by Allan Wang on 2017-08-02. + */ +class NoAnimatorAdd(override var itemDelayFactor: Float = 0f) : KauAnimatorAdd { + + override fun animationPrepare(holder: RecyclerView.ViewHolder): View.() -> Unit = {} + + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = {} + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { } + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} + +class NoAnimatorRemove(override var itemDelayFactor: Float = 0f) : KauAnimatorRemove { + + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { } + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = {} + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} + +class NoAnimatorChange : KauAnimatorChange { + + override fun changeOldAnimation( + holder: RecyclerView.ViewHolder, + changeInfo: BaseItemAnimator.ChangeInfo + ): ViewPropertyAnimator.() -> Unit = { } + + override fun changeNewAnimation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { } + + override fun changeAnimationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { alpha = 1f } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/SlideAnimator.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/SlideAnimator.kt new file mode 100644 index 0000000..55d5b2e --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/animators/SlideAnimator.kt @@ -0,0 +1,83 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.animators + +import android.view.View +import android.view.ViewPropertyAnimator +import androidx.recyclerview.widget.RecyclerView +import ca.allanwang.kau.utils.KAU_BOTTOM +import ca.allanwang.kau.utils.KAU_LEFT +import ca.allanwang.kau.utils.KAU_RIGHT +import ca.allanwang.kau.utils.KAU_TOP + +/** + * Created by Allan Wang on 2017-07-11. + */ +class SlideAnimatorAdd(val fromEdge: Int, val slideFactor: Float = 1f, override var itemDelayFactor: Float = 0.125f) : + KauAnimatorAdd { + + override fun animationPrepare(holder: RecyclerView.ViewHolder): View.() -> Unit = { + when (fromEdge) { + KAU_TOP -> translationY = slideFactor * -height + KAU_LEFT -> translationX = slideFactor * -width + KAU_BOTTOM -> translationY = slideFactor * height + KAU_RIGHT -> translationX = slideFactor * width + else -> throw KauAnimatorException("Invalid edge flag used in Slide Animator; use one of KAU_*") + } + alpha = 0f + } + + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { + translationY(0f) + translationX(0f) + alpha(1f) + } + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { + translationY = 0f + translationX = 0f + alpha = 1f + } + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} + +class SlideAnimatorRemove( + val fromEdge: Int, + val slideFactor: Float = 1f, + override var itemDelayFactor: Float = 0.125f +) : KauAnimatorRemove { + override fun animation(holder: RecyclerView.ViewHolder): ViewPropertyAnimator.() -> Unit = { + with(holder.itemView) { + when (fromEdge) { + KAU_TOP -> translationY(slideFactor * -height) + KAU_LEFT -> translationX(slideFactor * -width) + KAU_BOTTOM -> translationY(slideFactor * height) + KAU_RIGHT -> translationX(slideFactor * width) + else -> throw KauAnimatorException("Invalid edge flag used in Slide Animator; use one of KAU_*") + } + } + alpha(0f) + } + + override fun animationCleanup(holder: RecyclerView.ViewHolder): View.() -> Unit = { + translationY = 0f + translationX = 0f + alpha = 1f + } + + override fun getDelay(remove: Long, move: Long, change: Long): Long = 0L +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt new file mode 100644 index 0000000..6e33833 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/CardIItem.kt @@ -0,0 +1,140 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.iitems + +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.view.View +import android.widget.Button +import android.widget.ImageView +import android.widget.LinearLayout +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import ca.allanwang.kau.adapter.R +import ca.allanwang.kau.adapters.ThemableIItem +import ca.allanwang.kau.adapters.ThemableIItemDelegate +import ca.allanwang.kau.utils.INVALID_ID +import ca.allanwang.kau.utils.drawable +import ca.allanwang.kau.utils.gone +import ca.allanwang.kau.utils.string +import ca.allanwang.kau.utils.toDrawable +import ca.allanwang.kau.utils.visible +import com.mikepenz.fastadapter.FastAdapter +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.listeners.ClickEventHook +import com.mikepenz.iconics.typeface.IIcon + +/** + * Created by Allan Wang on 2017-06-28. + * + * Simple generic card item with an icon, title, description and button + * The icon and button are hidden by default unless values are given + */ +class CardIItem( + val builder: Config.() -> Unit = {} +) : KauIItem( + R.layout.kau_iitem_card, ::ViewHolder, R.id.kau_item_card +), ThemableIItem by ThemableIItemDelegate() { + + companion object { + fun bindClickEvents(fastAdapter: FastAdapter>) { + fastAdapter.withEventHook(object : ClickEventHook>() { + override fun onBindMany(viewHolder: RecyclerView.ViewHolder): List? { + return if (viewHolder is ViewHolder) listOf(viewHolder.card, viewHolder.button) else null + } + + override fun onClick(v: View, position: Int, adapter: FastAdapter>, item: IItem<*, *>) { + if (item !is CardIItem) return + with(item.configs) { + when (v.id) { + R.id.kau_card_container -> cardClick?.invoke() + R.id.kau_card_button -> buttonClick?.invoke() + else -> { + } + } + } + } + }) + } + } + + val configs = Config().apply { builder() } + + class Config { + var title: String? = null + var titleRes: Int = INVALID_ID + var desc: String? = null + var descRes: Int = INVALID_ID + var button: String? = null + var buttonRes: Int = INVALID_ID + var buttonClick: (() -> Unit)? = null + var cardClick: (() -> Unit)? = null + var image: Drawable? = null + var imageIIcon: IIcon? = null + var imageIIconColor: Int = Color.WHITE + var imageRes: Int = INVALID_ID + } + + override fun bindView(holder: ViewHolder, payloads: MutableList) { + super.bindView(holder, payloads) + with(holder.itemView.context) context@{ + with(configs) { + holder.title.text = string(titleRes, title) + val descText = string(descRes, desc) + if (descText != null) holder.description.visible().text = descText + val buttonText = string(buttonRes, button) + if (buttonText != null) { + holder.bottomRow.visible() + holder.button.text = buttonText + } + val icon = drawable(imageRes) { + imageIIcon?.toDrawable(this@context, sizeDp = 24, color = imageIIconColor) + ?: image + } + if (icon != null) holder.icon.visible().setImageDrawable(icon) + } + with(holder) { + bindTextColor(title) + bindTextColorSecondary(description) + bindAccentColor(button) + if (configs.imageIIcon != null) bindIconColor(icon) + bindBackgroundRipple(card) + } + } + } + + override fun unbindView(holder: ViewHolder) { + super.unbindView(holder) + with(holder) { + icon.gone().setImageDrawable(null) + title.text = null + description.gone().text = null + bottomRow.gone() + button.setOnClickListener(null) + card.setOnClickListener(null) + } + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + val card: CardView = v.findViewById(R.id.kau_card_container) + val icon: ImageView = v.findViewById(R.id.kau_card_image) + val title: TextView = v.findViewById(R.id.kau_card_title) + val description: TextView = v.findViewById(R.id.kau_card_description) + val bottomRow: LinearLayout = v.findViewById(R.id.kau_card_bottom_row) + val button: Button = v.findViewById(R.id.kau_card_button) + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt new file mode 100644 index 0000000..2c488b1 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/HeaderIItem.kt @@ -0,0 +1,59 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.iitems + +import android.view.View +import android.widget.TextView +import androidx.cardview.widget.CardView +import androidx.recyclerview.widget.RecyclerView +import ca.allanwang.kau.adapter.R +import ca.allanwang.kau.adapters.ThemableIItem +import ca.allanwang.kau.adapters.ThemableIItemDelegate +import ca.allanwang.kau.utils.INVALID_ID +import ca.allanwang.kau.utils.string + +/** + * Created by Allan Wang on 2017-06-28. + * + * Simple Header with lots of padding on the top + * Contains only one text view + */ +class HeaderIItem( + text: String? = null, + var textRes: Int = INVALID_ID +) : KauIItem( + R.layout.kau_iitem_header, { ViewHolder(it) }, R.id.kau_item_header_big_margin_top +), ThemableIItem by ThemableIItemDelegate() { + + var text: String = text ?: "Header Placeholder" + + override fun bindView(holder: ViewHolder, payloads: MutableList) { + super.bindView(holder, payloads) + holder.text.text = holder.itemView.context.string(textRes, text) + bindTextColor(holder.text) + bindBackgroundColor(holder.container) + } + + override fun unbindView(holder: ViewHolder) { + super.unbindView(holder) + holder.text.text = null + } + + class ViewHolder(v: View) : RecyclerView.ViewHolder(v) { + val text: TextView = v.findViewById(R.id.kau_header_text) + val container: CardView = v.findViewById(R.id.kau_header_container) + } +} diff --git a/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt new file mode 100644 index 0000000..c66dc01 --- /dev/null +++ b/fastadapter/src/main/kotlin/ca/allanwang/kau/iitems/KauIItem.kt @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.iitems + +import android.annotation.SuppressLint +import android.view.View +import androidx.annotation.LayoutRes +import androidx.recyclerview.widget.RecyclerView +import com.mikepenz.fastadapter.IClickable +import com.mikepenz.fastadapter.IItem +import com.mikepenz.fastadapter.items.AbstractItem + +/** + * Created by Allan Wang on 2017-07-03. + * + * Kotlin implementation of the [AbstractItem] to make things shorter + * If only one iitem type extends the given [layoutRes], you may use it as the type and not worry about another id + */ +open class KauIItem( + @param:LayoutRes private val layoutRes: Int, + private val viewHolder: (v: View) -> VH, + private val type: Int = layoutRes +) : AbstractItem() where Item : IItem<*, *>, Item : IClickable<*> { + @SuppressLint("ResourceType") + final override fun getType(): Int = type + + final override fun getViewHolder(v: View): VH = viewHolder(v) + final override fun getLayoutRes(): Int = layoutRes +} diff --git a/fastadapter/src/main/res/layout/kau_iitem_card.xml b/fastadapter/src/main/res/layout/kau_iitem_card.xml new file mode 100644 index 0000000..6bae0fe --- /dev/null +++ b/fastadapter/src/main/res/layout/kau_iitem_card.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + +