From 02e1dbc84425b0ac7f771c82f70444f742397452 Mon Sep 17 00:00:00 2001 From: Allan Wang Date: Mon, 7 Aug 2017 22:20:57 -0700 Subject: [PATCH] Release 3.3.0 (#32) * Rewrite Logger (#29) * Remove dependency on timber * Update logger * Reorder throwabl * Fix lint * Update readme * Blank target * Create Zip (#30) * Finish zips with tests * Finalize * Update changelog * Add log hooks * Open most logging functions * Remap kpref items (#31) * Update readme * Generate files and prepare release * Kpref - --- KPref | 0 README.md | 13 +-- .../allanwang/kau/adapters/ChainedAdapters.kt | 4 +- .../kau/animators/BaseItemAnimator.java | 2 +- android-lib.gradle | 3 +- .../kau/ui/widgets/InkPageIndicator.java | 3 +- core/README.md | 15 +-- core/build.gradle | 4 +- .../kotlin/ca/allanwang/kau/kotlin/Zip.kt | 90 +++++++++++++++++ .../kotlin/ca/allanwang/kau/logging/KL.kt | 2 +- .../ca/allanwang/kau/logging/TimberLogger.kt | 74 +++++++++++--- .../kotlin/ca/allanwang/kau/utils/Utils.kt | 8 -- .../ca/allanwang/kau/utils/ViewUtils.kt | 20 ++-- .../kotlin/ca/allanwang/kau/kotlin/ZipTest.kt | 72 ++++++++++++++ docs/Changelog.md | 5 + gradle.properties | 7 +- kpref-activity/README.md | 16 ++- kpref-activity/build.gradle | 1 - .../kau/kpref/activity/KPrefActivity.kt | 98 +++++++++---------- .../kau/kpref/activity/KPrefBinder.kt | 29 +----- .../src/main/res/layout/kau_pref_activity.xml | 16 +-- .../main/kotlin/ca/allanwang/kau/sample/SL.kt | 8 -- .../ca/allanwang/kau/sample/SampleApp.kt | 4 +- sample/src/main/res/xml/kau_changelog.xml | 6 ++ .../ca/allanwang/kau/searchview/SearchView.kt | 3 +- 25 files changed, 338 insertions(+), 165 deletions(-) create mode 100644 KPref create mode 100644 core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt create mode 100644 core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt delete mode 100644 sample/src/main/kotlin/ca/allanwang/kau/sample/SL.kt diff --git a/KPref b/KPref new file mode 100644 index 000000000..e69de29bb diff --git a/README.md b/README.md index 5092d771d..f895eb6d8 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ + KAU + An extensive collection of Kotlin Android Utils @@ -72,7 +74,7 @@ dependencies { * [CollapsibleViewDelegate](core#collapsible-view-delegate) * [Swipe](core#swipe) * [Debounce](core#debounce) - * [Timber Logger](core#timber-logger) + * [KAU Logger](core#kau-logger) * [Email Builder](core#email-builder) * [Extension Functions](core#extension-functions) * [Lazy Resettable](core#lazy-resettable) @@ -81,7 +83,6 @@ dependencies { [`Material Dialogs (core)`](https://github.com/afollestad/material-dialogs), [`Iconics`](https://github.com/mikepenz/Android-Iconics), [`Anko`](https://github.com/Kotlin/anko), -[`Timber`](https://github.com/JakeWharton/timber), [`Kotlin stdlib`](https://kotlinlang.org/api/latest/jvm/stdlib/) ## [Core UI](core-ui#readme) @@ -89,12 +90,12 @@ dependencies { * Includes `:core`, `:adapter` ## [About](about#readme) -* Implementation of an overlaying about section, along with automatic lib detection; also includes the lib strings for KAU +* 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`, [`About Libraries`](https://github.com/mikepenz/AboutLibraries) ## [Adapter](adapter#readme) -* Kotlin bindings for the fast adapter, as well as RecyclerView animations +* Kotlin bindings for the fast adapter, as well as modularized RecyclerView animations * Includes `:core`, [`Fast Adapter`](https://github.com/mikepenz/FastAdapter) @@ -104,8 +105,8 @@ dependencies { ([`Material Dialogs (commons)`](https://github.com/afollestad/material-dialogs)) ## [KPref Activity](kpref-activity#readme) -* Fully programmatic implementation of a Preference Activity, backed by RecyclerViews -* Includes `:core-ui`, `:adapter`, `colorpicker` +* Fully programmatic implementation of a Preference Activity, backed by a RecyclerView +* Includes `:core`, `:adapter`, `:colorpicker` ## [Media Picker](mediapicker#readme) * Fully functional image and video pickers, both as an overlay and as a requested activity. diff --git a/adapter/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt b/adapter/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt index e1c5c1876..2da0cac49 100644 --- a/adapter/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt +++ b/adapter/src/main/kotlin/ca/allanwang/kau/adapters/ChainedAdapters.kt @@ -2,6 +2,7 @@ package ca.allanwang.kau.adapters import android.support.v7.widget.LinearLayoutManager import android.support.v7.widget.RecyclerView +import ca.allanwang.kau.utils.withLinearAdapter import com.mikepenz.fastadapter.IItem import com.mikepenz.fastadapter.adapters.HeaderAdapter import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter @@ -55,8 +56,7 @@ class ChainedAdapters(vararg items: Pair>) { recycler = recyclerView indexStack.push(0) with(recyclerView) { - layoutManager = LinearLayoutManager(context) - adapter = chain.first().second + withLinearAdapter(chain.first().second) addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(rv: RecyclerView, dx: Int, dy: Int) { super.onScrolled(rv, dx, dy) diff --git a/adapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java b/adapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java index 69c2cf345..06b8df693 100644 --- a/adapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java +++ b/adapter/src/main/kotlin/ca/allanwang/kau/animators/BaseItemAnimator.java @@ -485,7 +485,7 @@ public void changeAnimation(ViewHolder oldHolder, ViewHolder newHolder, int from public abstract ViewPropertyAnimator changeNewAnimation(ViewHolder holder); /** - * the cleanup method if the animation needs to be stopped. and tro prepare for the next view + * the cleanup method if the animation needs to be stopped. and to prepare for the next view * * @param holder */ diff --git a/android-lib.gradle b/android-lib.gradle index 46b74a82e..a8417c063 100644 --- a/android-lib.gradle +++ b/android-lib.gradle @@ -36,8 +36,7 @@ android { lintOptions { warningsAsErrors true - disable 'LogNotTimber', - 'UnusedResources', + disable 'UnusedResources', 'ContentDescription', 'RtlSymmetry', 'RtlHardcoded', diff --git a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/InkPageIndicator.java b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/InkPageIndicator.java index 65eb5b7bf..d56c85ef6 100644 --- a/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/InkPageIndicator.java +++ b/core-ui/src/main/kotlin/ca/allanwang/kau/ui/widgets/InkPageIndicator.java @@ -30,7 +30,6 @@ import android.support.annotation.ColorInt; import android.support.v4.view.ViewPager; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.view.animation.Interpolator; @@ -642,7 +641,7 @@ public void onAnimationEnd(Animator animation) { private void setJoiningFraction(int leftDot, float fraction) { if (leftDot < joiningFractions.length) { - if (leftDot == 1) KL.INSTANCE.v("PageIndicator dot 1 fraction:\t$fraction"); + if (leftDot == 1) KL.INSTANCE.v("PageIndicator dot 1 fraction:\t" + fraction, null); joiningFractions[leftDot] = fraction; postInvalidateOnAnimation(); diff --git a/core/README.md b/core/README.md index 67960822d..4cb68d066 100644 --- a/core/README.md +++ b/core/README.md @@ -13,7 +13,7 @@ * [CollapsibleViewDelegate](#collapsible-view-delegate) * [Swipe](#swipe) * [Debounce](#debounce) -* [Timber Logger](#timber-logger) +* [KAU Logger](#kau-logger) * [Email Builder](#email-builder) * [Extension Functions](#extension-functions) * [Lazy Resettable](#lazy-resettable) @@ -58,7 +58,7 @@ The object inherits the initializer method `fun initialize(c: Context, preferenc There is also a `reset()` method to clear the local values and have them retrieve from the SharedPreference again. In shared preferences, we often require a boolean that returns true once, so we can use it to showcase views or display prompts on the first load. -Kpref supports special preferences like these through the `KPrefSingleDelgate` +KPref supports special preferences like these through the `KPrefSingleDelgate` It can be used in a KPref like so: @@ -193,11 +193,14 @@ KAU offers extensions to easily convert or create functions into debouncables. Simply call `debounce` and specify your interval on an existing function, or with a new function. -## Timber Logger +## KAU Logger -[Timber](https://github.com/JakeWharton/timber)'s DebugTree uses the tag to specify the current class that is being logged. -To add the tag directly in the message, create an object that extends the TimberLogger class with the tag name as the argument. -Along with the timber methods (`v`, `i`, `d`, `e`), Timber Logger also supports `eThrow` to wrap a String in a throwable +`KauLogger` can be extended by an object to implement a logger with a permanent tag. +It's methods are the same as the main logger, along with the following: +* Logging methods have two inputs, the main text and private text. +If private text is disabled (default), it will not be logged. +* The loggers have toggles to enable logging as a whole and to filter out priorities that we wish to ignore. +* Has some other functions, like logging the current thread. ## Email Builder diff --git a/core/build.gradle b/core/build.gradle index 0960d5940..cdd785918 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -16,9 +16,7 @@ dependencies { compile "com.mikepenz:google-material-typeface:${IICON_GOOGLE}.original@aar" compile "com.afollestad.material-dialogs:core:${MATERIAL_DIALOG}" - - compile "com.jakewharton.timber:timber:${TIMBER}" - + compile "org.jetbrains.anko:anko-commons:${ANKO}" } diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt new file mode 100644 index 000000000..cff520f46 --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt @@ -0,0 +1,90 @@ +package ca.allanwang.kau.kotlin + +import org.jetbrains.anko.doAsync +import java.util.concurrent.atomic.AtomicInteger + +/** + * Created by Allan Wang on 2017-08-06. + * + * Collection of zip methods that aim to replicate + * Reactive Zips + * For unit returning functions + * + * Typically, the functions will execute asynchronously and call their given callbacks when finished. + * Once all callbacks are called, the final onFinish callback will be executed. + * + * There is also a helper zipper to wrap synchronous functions with Anko's doAsync to achieve the same results + * + * Note that not wrapping synchronous functions will render these methods useless, + * as you can simply define an inline callback after all functions are finished + */ + +/** + * Callback which will only execute the first time + */ +open class ZipCallbackBase { + var completed: Boolean = false + + inline operator fun invoke(callback: () -> Unit) { + if (completed) return + completed = true + callback() + } +} + +class ZipCallback(val onReceived: (T) -> Unit) : ZipCallbackBase() { + operator fun invoke(result: T) = invoke { onReceived(result) } +} + +class ZipEmptyCallback(val onReceived: () -> Unit) : ZipCallbackBase() { + operator fun invoke() = invoke(onReceived) +} + +/** + * Given a default result, a series of tasks, and a finished callback, + * this method will run all tasks and wait until all tasks emit a response + * The response will then be sent back to the callback + * + * ALl tasks must invoke the task callback for [onFinished] to execute + */ +inline fun Collection<(ZipCallback) -> Unit>.zip( + defaultResult: T, crossinline onFinished: (results: Array) -> Unit +) { + val result = Array(size) { defaultResult } + val countDown = AtomicInteger(size) + forEachIndexed { index, asyncFun -> + asyncFun(ZipCallback { + result[index] = it + if (countDown.decrementAndGet() <= 0) + onFinished(result) + }) + } +} + +/** + * Simplified zip method with no finished callback arguments + */ +inline fun Collection<(ZipEmptyCallback) -> Unit>.zip(crossinline onFinished: () -> Unit) { + val countDown = AtomicInteger(size) + forEach { asyncFun -> + asyncFun(ZipEmptyCallback { + if (countDown.decrementAndGet() <= 0) + onFinished() + }) + } +} + +/** + * Converts a collection of synchronous tasks to asynchronous tasks with a common callback + */ +inline fun Collection<() -> Unit>.zipAsync(crossinline onFinished: () -> Unit) { + map { synchronousFun -> + { + callback: ZipEmptyCallback -> + doAsync { + synchronousFun() + callback() + }; Unit + } + }.zip(onFinished) +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt b/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt index 4fa336070..24146b01d 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt @@ -3,4 +3,4 @@ package ca.allanwang.kau.logging /** * Created by Allan Wang on 2017-06-19. */ -object KL : TimberLogger("KAU") \ No newline at end of file +object KL : KauLogger("KAU") \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt b/core/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt index 4cf566a63..2fbecf5e2 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/logging/TimberLogger.kt @@ -2,23 +2,71 @@ package ca.allanwang.kau.logging -import timber.log.Timber +import android.os.Looper +import android.util.Log /** * Created by Allan Wang on 2017-05-28. * - * Timber extension that will embed the tag as part of the message for each log item + * Base logger class with a predefined tag + * This may be extended by an object to effectively replace [Log] */ -open class TimberLogger(tag: String) { - val TAG = "$tag: %s" - inline fun e(s: String) = Timber.e(TAG, s) - inline fun e(t: Throwable?, s: String = "error") = if (t == null) e(s) else Timber.e(t, TAG, s) - inline fun d(s: String) = Timber.d(TAG, s) - inline fun i(s: String) = Timber.i(TAG, s) - inline fun v(s: String) = Timber.v(TAG, s) - inline fun eThrow(s: String) = e(Throwable(s)) -// fun plant() { -// Timber.plant(Timber.Tree()) -// } +open class KauLogger(val tag: String) { + + open var enabled = true + open var showPrivateText = false + + /** + * Filter pass-through to decide what we wish to log + * By default, we will ignore verbose and debug logs + * @returns {@code true} to log the message, {@code false} to ignore + */ + open var filter: (Int) -> Boolean = { it != Log.VERBOSE && it != Log.DEBUG } + + open fun disable(disable: Boolean = true): KauLogger { + enabled = !disable + return this + } + + open fun debug(enable: Boolean) { + filter = if (enable) { _ -> true } else { i -> i != Log.VERBOSE && i != Log.DEBUG } + showPrivateText = enable + } + + open fun log(priority: Int, message: String?, privateMessage: String?, t: Throwable? = null) { + if (!shouldLog(priority, message, privateMessage, t)) return + logImpl(priority, message, privateMessage, t) + } + + protected open fun shouldLog(priority: Int, message: String?, privateMessage: String?, t: Throwable?): Boolean + = enabled && filter(priority) + + protected open fun logImpl(priority: Int, message: String?, privateMessage: String?, t: Throwable?) { + var text = message ?: "" + if (showPrivateText && privateMessage != null) + text += "\n-\t$privateMessage" + if (t != null) Log.e(tag, text, t) + else if (text.isNotBlank()) Log.println(priority, tag, text) + } + + open fun v(text: String?, privateText: String? = null) = log(Log.VERBOSE, text, privateText) + open fun d(text: String?, privateText: String? = null) = log(Log.DEBUG, text, privateText) + open fun i(text: String?, privateText: String? = null) = log(Log.INFO, text, privateText) + open fun e(text: String?, privateText: String? = null) = log(Log.ERROR, text, privateText) + open fun a(text: String?, privateText: String? = null) = log(Log.ASSERT, text, privateText) + open fun e(t: Throwable?, text: String?, privateText: String? = null) = log(Log.ERROR, text, privateText, t) + open fun eThrow(text: String?) { + if (text != null) + e(Throwable(text), text) + } + + /** + * Log the looper + */ + open fun checkThread(id: Int) { + val name = Thread.currentThread().name + val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" + d("$id $status in the main thread - thread name: $name") + } } \ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt index e8f385a8c..2f3e9a504 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -38,14 +38,6 @@ annotation class KauUtils @KauUtils inline val Int.pxToDp: Int get() = (this / Resources.getSystem().displayMetrics.density).toInt() -/** - * Log whether current state is in the main thread - */ -@KauUtils fun checkThread(id: Int) { - val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" - KL.d("$id $status in the main thread") -} - /** * Converts minute value to string * Whole hours and days will be converted as such, otherwise it will default to x minutes diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt index 53d711dca..3620a4a88 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt @@ -211,13 +211,19 @@ inline val TextInputEditText.value: String get() = text.toString().trim() /** * Generates a recycler view with match parent and a linearlayoutmanager, since it's so commonly used */ -fun Context.fullLinearRecycler(rvAdapter: RecyclerView.Adapter<*>? = null, configs: RecyclerView.() -> Unit = {}): RecyclerView { - return RecyclerView(this).apply { - layoutManager = LinearLayoutManager(this@fullLinearRecycler) - layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT) - if (rvAdapter != null) adapter = rvAdapter - configs() - } +fun Context.fullLinearRecycler(rvAdapter: RecyclerView.Adapter<*>? = null, configs: RecyclerView.() -> Unit = {}) = RecyclerView(this).apply { + layoutManager = LinearLayoutManager(this@fullLinearRecycler) + layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.MATCH_PARENT) + if (rvAdapter != null) adapter = rvAdapter + configs() +} + +/** + * Sets a linear layout manager along with an adapter + */ +fun RecyclerView.withLinearAdapter(rvAdapter: RecyclerView.Adapter<*>) = apply { + layoutManager = LinearLayoutManager(context) + adapter = rvAdapter } /** diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt new file mode 100644 index 000000000..4a0414297 --- /dev/null +++ b/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt @@ -0,0 +1,72 @@ +package ca.allanwang.kau.kotlin + +import org.jetbrains.anko.doAsync +import org.junit.Test +import java.util.* +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import kotlin.test.assertTrue + +/** + * Created by Allan Wang on 2017-08-06. + */ +class ZipTest { + + val debug = false + + fun p(text: String) { + if (debug) println(text) + } + + @Test + fun basic() { + val start = System.currentTimeMillis() + val latch = CountDownLatch(1) + val rnd = Random() + (0..10).map { + { + callback: ZipCallback -> + doAsync { + val sleepTime = rnd.nextInt(100) + 200L + p("Task $it will sleep for ${sleepTime}ms") + Thread.sleep(sleepTime) + val finish = System.currentTimeMillis() + p("Task $it finished in ${finish - start}ms at $finish") + callback(it) + }; Unit + } + }.zip(-1) { + results -> + val finish = System.currentTimeMillis() + println("Results ${results.contentToString()} received in ${finish - start}ms at $finish") + assertTrue((0..10).toList().toTypedArray().contentEquals(results), "Basic zip results do not match") + assertTrue(finish - start < 1000L, "Basic zip does not seem to be running asynchronously") + latch.countDown() + + } + latch.await(1100, TimeUnit.MILLISECONDS) + } + + @Test + fun basicAsync() { + val start = System.currentTimeMillis() + val latch = CountDownLatch(1) + val rnd = Random() + (0..10).map { + { + val sleepTime = rnd.nextInt(100) + 200L + p("Task $it will sleep for ${sleepTime}ms") + Thread.sleep(sleepTime) + val finish = System.currentTimeMillis() + p("Task $it finished in ${finish - start}ms at $finish") + } + }.zipAsync { + val finish = System.currentTimeMillis() + println("Results received in ${finish - start}ms at $finish") + assertTrue(finish - start < 1000L, "BasicAsync does not seem to be wrapping the tasks asynchronously") + latch.countDown() + } + latch.await(1100, TimeUnit.MILLISECONDS) + } + +} \ No newline at end of file diff --git a/docs/Changelog.md b/docs/Changelog.md index 3c61dd352..0fe867a0a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -2,6 +2,11 @@ ## v3.3.0 * :core: Create debounce methods +* :core: Create zip methods +* :core: [Breaking] Logging base has been renamed to KauLogger and no longer depends on timber +* :kpref-activity: Rewrote binding logic to use only one recyclerview +* :kpref-activity: [Breaking] Removed sliding toolbar and use normal toolbar title +* :kpref-activity: Remove :core-ui: dependency * :searchview: [Breaking] remove reactive dependencies and stick with basic callbacks ## v3.2.5 diff --git a/gradle.properties b/gradle.properties index a778a6667..b73864288 100644 --- a/gradle.properties +++ b/gradle.properties @@ -23,7 +23,7 @@ TARGET_SDK=26 BUILD_TOOLS=26.0.1 ANDROID_SUPPORT_LIBS=26.0.0 -VERSION_NAME=3.2.6 +VERSION_NAME=3.3.0 KOTLIN=1.1.3-2 ABOUT_LIBRARIES=5.9.7 @@ -36,11 +36,6 @@ GLIDE=4.0.0 ICONICS=2.9.1 IICON_GOOGLE=3.0.1.1 MATERIAL_DIALOG=0.9.4.5 -RX_ANDROID=2.0.1 -RX_BINDING=2.0.0 -RX_JAVA=2.1.2 -RX_KOTLIN=2.1.0 -TIMBER=4.5.1 ESPRESSO=3.0.0 JUNIT=4.12 diff --git a/kpref-activity/README.md b/kpref-activity/README.md index 5ec94a690..015d0d8ed 100644 --- a/kpref-activity/README.md +++ b/kpref-activity/README.md @@ -39,9 +39,6 @@ An example of the adapter builder: ```kotlin override fun onCreateKPrefs(savedInstanceState: android.os.Bundle?): KPrefAdapterBuilder.() -> Unit = { - - textColor = { KPrefSample.textColor } // getter function so the new text color will be retrieved for every reload - accentColor = { KPrefSample.accentColor } header(R.string.header) @@ -66,4 +63,17 @@ override fun onCreateKPrefs(savedInstanceState: android.os.Bundle?): KPrefAdapte } } } +``` + +On top of per item configurations, `KPrefActivity` has some core attributes that you can define on creation. +It is done through the abstract function: + +```kotlin + + override fun kPrefCoreAttributes(): CoreAttributeContract.() -> Unit = { + textColor = { Prefs.textColor } // text color getter; refreshes automatically on reload + accentColor = { Prefs.accentColor } // accent color getter + // background color does not exist as it is done through the ripple canvas + } + ``` \ No newline at end of file diff --git a/kpref-activity/build.gradle b/kpref-activity/build.gradle index ab2a7de34..e0849d475 100644 --- a/kpref-activity/build.gradle +++ b/kpref-activity/build.gradle @@ -5,7 +5,6 @@ ext.kauSubModuleResourcePrefix = "kau_pref_" apply from: '../android-lib.gradle' dependencies { - compile project(':core-ui') compile project(':adapter') compile project(':colorpicker') } diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt index 3e1596f16..900b00442 100644 --- a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt +++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt @@ -3,49 +3,43 @@ package ca.allanwang.kau.kpref.activity import android.annotation.SuppressLint import android.os.Bundle import android.support.annotation.StringRes -import android.support.constraint.ConstraintLayout -import android.support.v7.app.AppCompatActivity import android.support.v7.widget.RecyclerView import android.support.v7.widget.Toolbar import android.view.View -import android.view.animation.Animation -import android.view.animation.AnimationUtils -import android.widget.FrameLayout -import android.widget.ViewAnimator +import ca.allanwang.kau.animators.KauAnimator +import ca.allanwang.kau.animators.SlideAnimatorAdd +import ca.allanwang.kau.animators.SlideAnimatorRemove import ca.allanwang.kau.internal.KauBaseActivity import ca.allanwang.kau.kpref.activity.items.KPrefItemCore import ca.allanwang.kau.ui.views.RippleCanvas -import ca.allanwang.kau.ui.widgets.TextSlider -import ca.allanwang.kau.utils.bindView -import ca.allanwang.kau.utils.resolveColor -import ca.allanwang.kau.utils.statusBarColor -import ca.allanwang.kau.utils.string +import ca.allanwang.kau.utils.* import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter +import org.jetbrains.anko.doAsync +import org.jetbrains.anko.uiThread +import java.util.* abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract { - val adapter: FastItemAdapter - @Suppress("UNCHECKED_CAST") - get() = recycler.adapter as FastItemAdapter - val recycler: RecyclerView - get() = prefHolder.currentView as RecyclerView - val container: ConstraintLayout by bindView(R.id.kau_container) + private val adapter: FastItemAdapter = FastItemAdapter() + private val recycler: RecyclerView by bindView(R.id.kau_recycler) val bgCanvas: RippleCanvas by bindView(R.id.kau_ripple) val toolbarCanvas: RippleCanvas by bindView(R.id.kau_toolbar_ripple) val toolbar: Toolbar by bindView(R.id.kau_toolbar) - val toolbarTitle: TextSlider by bindView(R.id.kau_toolbar_text) - val prefHolder: ViewAnimator by bindView(R.id.kau_holder) private lateinit var globalOptions: GlobalOptions + private val kprefStack = Stack>>() + /** + * Toggle sliding animations for the kpref items + */ var animate: Boolean = true - set(value) { - field = value - toolbarTitle.animationType = if (value) TextSlider.ANIMATION_SLIDE_HORIZONTAL else TextSlider.ANIMATION_NONE - } - private val SLIDE_IN_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_left) } - private val SLIDE_IN_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_in_right) } - private val SLIDE_OUT_LEFT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_left) } - private val SLIDE_OUT_RIGHT_ITEMS: Animation by lazy { AnimationUtils.loadAnimation(this, R.anim.kau_slide_out_right) } + private val recyclerAnimatorNext: KauAnimator by lazy { + KauAnimator(SlideAnimatorAdd(KAU_RIGHT, itemDelayFactor = 0f), + SlideAnimatorRemove(KAU_LEFT, itemDelayFactor = 0f)) + } + private val recyclerAnimatorPrev: KauAnimator by lazy { + KauAnimator(SlideAnimatorAdd(KAU_LEFT, itemDelayFactor = 0f), + SlideAnimatorRemove(KAU_RIGHT, itemDelayFactor = 0f)) + } /** * Core attribute builder that is consistent throughout all items @@ -69,43 +63,41 @@ abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract { statusBarColor = 0x30000000 toolbarCanvas.set(resolveColor(R.attr.colorPrimary)) bgCanvas.set(resolveColor(android.R.attr.colorBackground)) - prefHolder.animateFirstView = false //setup prefs val core = CoreAttributeBuilder() val builder = kPrefCoreAttributes() core.builder() globalOptions = GlobalOptions(core, this) - showNextPrefs(R.string.kau_settings, onCreateKPrefs(savedInstanceState)) + recycler.withLinearAdapter(adapter) + adapter.withSelectable(false) + .withOnClickListener { v, _, item, _ -> item.onClick(v, v.findViewById(R.id.kau_pref_inner_content)) } + showNextPrefs(R.string.kau_settings, onCreateKPrefs(savedInstanceState), true) } - override fun onPostCreate(savedInstanceState: Bundle?) { - super.onPostCreate(savedInstanceState) - } + override fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit) + = showNextPrefs(toolbarTitleRes, builder, false) - override fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit) { - val rv = RecyclerView(this).apply { - layoutParams = FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT) - setKPrefAdapter(globalOptions, builder) + private fun showNextPrefs(@StringRes toolbarTitleRes: Int, builder: KPrefAdapterBuilder.() -> Unit, first: Boolean) { + doAsync { + val items = KPrefAdapterBuilder(globalOptions) + builder(items) + kprefStack.push(toolbarTitleRes to items.list) + recycler.itemAnimator = if (animate && !first) recyclerAnimatorNext else null + uiThread { + adapter.clear() + adapter.add(items.list) + toolbar.setTitle(toolbarTitleRes) + } } - with(prefHolder) { - inAnimation = if (animate) SLIDE_IN_RIGHT_ITEMS else null - outAnimation = if (animate) SLIDE_OUT_LEFT_ITEMS else null - addView(rv) - showNext() - } - toolbarTitle.setNextText(string(toolbarTitleRes)) } override fun showPrevPrefs() { - val current = prefHolder.currentView - with(prefHolder) { - inAnimation = if (animate) SLIDE_IN_LEFT_ITEMS else null - outAnimation = if (animate) SLIDE_OUT_RIGHT_ITEMS else null - showPrevious() - removeView(current) - adapter.notifyAdapterDataSetChanged() - } - toolbarTitle.setPrevText() + kprefStack.pop() + val (title, list) = kprefStack.peek() + recycler.itemAnimator = if (animate) recyclerAnimatorPrev else null + adapter.clear() + adapter.add(list) + toolbar.setTitle(title) } fun reload(vararg index: Int) { @@ -128,7 +120,7 @@ abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract { } fun backPress(): Boolean { - if (!toolbarTitle.isRoot) { + if (kprefStack.size > 1) { showPrevPrefs() return true } diff --git a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt index 6048c1a05..b45e40635 100644 --- a/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt +++ b/kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefBinder.kt @@ -1,39 +1,13 @@ package ca.allanwang.kau.kpref.activity import android.support.annotation.StringRes -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView -import ca.allanwang.kau.R import ca.allanwang.kau.kpref.activity.items.* -import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter -import org.jetbrains.anko.doAsync -import org.jetbrains.anko.uiThread /** * Created by Allan Wang on 2017-06-08. * * Houses all the components that can be called externally to setup the kpref mainAdapter */ - -/** - * Base extension that will register the layout manager and mainAdapter with the given items - * Returns FastAdapter - */ -fun RecyclerView.setKPrefAdapter(globalOptions: GlobalOptions, builder: KPrefAdapterBuilder.() -> Unit): FastItemAdapter { - layoutManager = LinearLayoutManager(context) - val adapter = FastItemAdapter() - adapter.withOnClickListener { v, _, item, _ -> item.onClick(v, v.findViewById(R.id.kau_pref_inner_content)) } - this.adapter = adapter - doAsync { - val items = KPrefAdapterBuilder(globalOptions) - builder.invoke(items) - uiThread { - adapter.add(items.list) - } - } - return adapter -} - @DslMarker annotation class KPrefMarker @@ -70,6 +44,9 @@ class GlobalOptions(core: CoreAttributeContract, activity: KPrefActivityContract * Contains DSLs for every possible item * The arguments are all the mandatory values plus an optional builder housing all the possible configurations * The mandatory values are final so they cannot be edited in the builder + * + * This function will be called asynchronously, so don't worry about blocking the thread + * The recycler will only animate once this is completed though */ @KPrefMarker class KPrefAdapterBuilder(val globalOptions: GlobalOptions) { diff --git a/kpref-activity/src/main/res/layout/kau_pref_activity.xml b/kpref-activity/src/main/res/layout/kau_pref_activity.xml index 19320b8b6..7651e5a07 100644 --- a/kpref-activity/src/main/res/layout/kau_pref_activity.xml +++ b/kpref-activity/src/main/res/layout/kau_pref_activity.xml @@ -1,7 +1,6 @@ @@ -22,16 +21,7 @@ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - - - + app:layout_constraintTop_toTopOf="parent" /> - + + + + + + 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 a98b2f604..cc5628926 100644 --- a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt +++ b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt @@ -229,14 +229,13 @@ class SearchView @JvmOverloads constructor( tintBackground(configs.backgroundColor) with(recycler) { isNestedScrollingEnabled = false - layoutManager = LinearLayoutManager(context) + withLinearAdapter(this@SearchView.adapter) addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) if (newState == RecyclerView.SCROLL_STATE_DRAGGING) hideKeyboard() } }) - adapter = this@SearchView.adapter itemAnimator = null } with(adapter) {