aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAllan Wang <me@allanwang.ca>2018-12-24 20:05:06 -0500
committerGitHub <noreply@github.com>2018-12-24 20:05:06 -0500
commit8447b1ae8ce89b3f1bbe79dbae8847d901831c12 (patch)
treece516950e452581766e905ead32970d891bb46f6
parent701b94ab09ff53aca682fac6c4ef5364566339be (diff)
downloadkau-8447b1ae8ce89b3f1bbe79dbae8847d901831c12.tar.gz
kau-8447b1ae8ce89b3f1bbe79dbae8847d901831c12.tar.bz2
kau-8447b1ae8ce89b3f1bbe79dbae8847d901831c12.zip
Enhancement/coroutines (#180)
* Add coroutine dependency * Add coroutines to kprefactivity * Change base job to supervisor * Update coroutines for faq * Update changelog * Use preloading in media picker core * Make test logging internal * Remove anko
-rw-r--r--README.md3
-rw-r--r--about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt24
-rw-r--r--buildSrc/src/main/groovy/ca/allanwang/kau/Versions.groovy6
-rw-r--r--core/README.md4
-rw-r--r--core/build.gradle4
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt30
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt105
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt7
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt13
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt55
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt27
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt3
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt21
-rw-r--r--core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt71
-rw-r--r--core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt83
-rw-r--r--docs/Changelog.md6
-rw-r--r--docs/Migration.md13
-rw-r--r--kpref-activity/src/main/kotlin/ca/allanwang/kau/kpref/activity/KPrefActivity.kt31
-rw-r--r--mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt33
-rw-r--r--sample/src/main/res/xml/kau_changelog.xml10
-rw-r--r--searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt2
21 files changed, 250 insertions, 301 deletions
diff --git a/README.md b/README.md
index 4143ca8..6fe7590 100644
--- a/README.md
+++ b/README.md
@@ -103,10 +103,9 @@ This means that you'll need to explicitly include each submodule you'd like to u
* [Extension Functions](core#extension-functions)
* [Lazy Resettable](core#lazy-resettable)
* Includes
-[`AppCompat`](https://developer.android.com/topic/libraries/support-library/index.html),
+[`AndroidX Components`](https://developer.android.com/topic/libraries/support-library/index.html),
[`Material Dialogs (core)`](https://github.com/afollestad/material-dialogs),
[`Iconics`](https://github.com/mikepenz/Android-Iconics),
-[`Anko`](https://github.com/Kotlin/anko),
[`Kotlin stdlib`](https://kotlinlang.org/api/latest/jvm/stdlib/)
## [Core UI](core-ui#readme)
diff --git a/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt b/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt
index 35c1322..f77bcf2 100644
--- a/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt
+++ b/about/src/main/kotlin/ca/allanwang/kau/about/AboutPanelDelegate.kt
@@ -35,8 +35,9 @@ import ca.allanwang.kau.utils.withMarginDecoration
import ca.allanwang.kau.xml.kauParseFaq
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.fastadapter.IItem
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
/**
* Created by Allan Wang on 2017-08-02.
@@ -174,13 +175,16 @@ open class AboutPanelLibs : AboutPanelRecycler() {
}
override fun loadItems(activity: AboutActivityBase, position: Int) {
- doAsync {
- with(activity) {
- items =
- getLibraries(if (rClass == null) Libs(activity) else Libs(this, Libs.toStringArray(rClass.fields)))
- .map(::LibraryIItem)
+ with(activity) {
+ launch {
+ items = async {
+ getLibraries(
+ if (rClass == null) Libs(activity)
+ else Libs(activity, Libs.toStringArray(rClass.fields))
+ ).map(::LibraryIItem)
+ }.await()
if (pageStatus[position] == 1)
- uiThread { addItems(activity, position) }
+ addItems(activity, position)
}
}
}
@@ -202,8 +206,8 @@ open class AboutPanelFaqs : AboutPanelRecycler() {
override fun loadItems(activity: AboutActivityBase, position: Int) {
with(activity) {
- kauParseFaq(configs.faqXmlRes, configs.faqParseNewLine) {
- items = it.map(::FaqIItem)
+ launch {
+ items = async { kauParseFaq(configs.faqXmlRes, configs.faqParseNewLine) }.await().map(::FaqIItem)
if (pageStatus[position] == 1)
addItems(activity, position)
}
diff --git a/buildSrc/src/main/groovy/ca/allanwang/kau/Versions.groovy b/buildSrc/src/main/groovy/ca/allanwang/kau/Versions.groovy
index a85fe97..e6f3cd7 100644
--- a/buildSrc/src/main/groovy/ca/allanwang/kau/Versions.groovy
+++ b/buildSrc/src/main/groovy/ca/allanwang/kau/Versions.groovy
@@ -26,12 +26,12 @@ class Versions {
// https://kotlinlang.org/docs/reference/using-gradle.html
static def kotlin = '1.3.11'
+ // https://github.com/Kotlin/kotlinx.coroutines/releases
+ static def coroutines = '1.0.1'
+
// https://github.com/mikepenz/AboutLibraries/releases
static def aboutLibraries = '6.2.0'
- // https://github.com/Kotlin/anko/releases
- static def anko = '0.10.5'
-
// https://github.com/wasabeef/Blurry/releases
static def blurry = '2.1.1'
diff --git a/core/README.md b/core/README.md
index 39899dd..b9a10f5 100644
--- a/core/README.md
+++ b/core/README.md
@@ -131,6 +131,9 @@ These variants are weakly held in the private `KotterknifeRegistry` object, and
values through the `Kotterknife.reset` method. This is typically useful for Fragments, as they do not follow
the same lifecycle as Activities and Views.
+Note that this is useful for views that have ids in multiple layout files or in `id.xml` files.
+Kotlin has another solution, [`kotlin-android-extensions`](https://kotlinlang.org/docs/tutorials/android-plugin.html), which is more convenient.
+
## Ripple Canvas
Ripple canvas provides a way to create simultaneous ripples against a background color.
@@ -210,7 +213,6 @@ Include your email and subject, along with other optional configurations such as
## Extension Functions
> "[Extensions](https://kotlinlang.org/docs/reference/extensions.html) provide the ability to extend a class with new functionality without having to inherit from the class"
-<br/>Note that since KAU depends on [ANKO](https://github.com/Kotlin/anko), all of the extensions in its core package is also in KAU.
KAU's vast collection of extensions is one of its strongest features.
There are too many to explain here, but you may check out the [utils package](https://github.com/AllanWang/KAU/tree/master/core/src/main/kotlin/ca/allanwang/kau/utils)
diff --git a/core/build.gradle b/core/build.gradle
index a57ee6e..3ac4f36 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -11,12 +11,12 @@ dependencies {
api "androidx.constraintlayout:constraintlayout:${kau.constraintLayout}"
api "com.google.android.material:material:${kau.googleMaterial}"
+ api "org.jetbrains.kotlinx:kotlinx-coroutines-android:${kau.coroutines}"
+
api "com.mikepenz:iconics-core:${kau.iconics}@aar"
api "com.mikepenz:google-material-typeface:${kau.iconicsGoogle}.original@aar"
api "com.afollestad.material-dialogs:core:${kau.materialDialog}"
-
- api "org.jetbrains.anko:anko-commons:${kau.anko}"
}
apply from: '../artifacts.gradle'
diff --git a/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt b/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt
index 85e711b..bf977f2 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt
@@ -15,8 +15,14 @@
*/
package ca.allanwang.kau.internal
+import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
+import kotlin.coroutines.CoroutineContext
/**
* Created by Allan Wang on 2017-08-01.
@@ -26,11 +32,31 @@ import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult
* Ensures that some singleton methods are called.
* This is simply a convenience class;
* you can always copy and paste this to your own class.
+ *
+ * This also implements [CoroutineScope] that adheres to the activity lifecycle.
+ * Note that by default, [SupervisorJob] is used, to avoid exceptions in one child from affecting that of another.
+ * The default job can be overridden within [defaultJob]
*/
-abstract class KauBaseActivity : AppCompatActivity() {
+abstract class KauBaseActivity : AppCompatActivity(), CoroutineScope {
+
+ open lateinit var job: Job
+ override val coroutineContext: CoroutineContext
+ get() = Dispatchers.Main + job
+
+ open fun defaultJob(): Job = SupervisorJob()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ job = defaultJob()
+ }
+
+ override fun onDestroy() {
+ job.cancel()
+ super.onDestroy()
+ }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
kauOnRequestPermissionsResult(permissions, grantResults)
}
-}
+} \ No newline at end of file
diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt
deleted file mode 100644
index b767b30..0000000
--- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * 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.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
- * <a href="http://reactivex.io/documentation/operators/zip.html">Reactive Zips</a>
- * 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<T>(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 <reified T> Collection<(ZipCallback<T>) -> Unit>.zip(
- defaultResult: T,
- crossinline onFinished: (results: Array<T>) -> 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 f92edb3..52e5415 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt
@@ -22,4 +22,9 @@ import ca.allanwang.kau.BuildConfig
*
* Internal KAU logger
*/
-object KL : KauLogger("KAU", { BuildConfig.DEBUG })
+object KL : KauLogger("KAU", { BuildConfig.DEBUG }) {
+ internal inline fun test(message: () -> Any?) {
+ if (BuildConfig.DEBUG)
+ d { "Test1234 ${message()}" }
+ }
+}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt
index a655e5b..3dd4bb9 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt
@@ -28,13 +28,13 @@ import android.os.Build
import android.os.Bundle
import android.view.Menu
import android.view.View
+import android.view.ViewGroup
import androidx.annotation.ColorInt
import androidx.annotation.RequiresApi
import androidx.annotation.StringRes
import ca.allanwang.kau.R
import com.google.android.material.snackbar.Snackbar
import com.mikepenz.iconics.typeface.IIcon
-import org.jetbrains.anko.contentView
/**
* Created by Allan Wang on 2017-06-21.
@@ -160,6 +160,17 @@ inline fun Activity.showKeyboard() {
currentFocus?.showKeyboard()
}
+/**
+ * Gets the view set by [Activity.setContentView] if it exists.
+ *
+ * Taken courtesy of <a href="https://github.com/Kotlin/anko">Anko</a>
+ *
+ * Previously, Anko was a dependency in KAU, but has been removed on 12/24/2018
+ * as most of the methods weren't used
+ */
+inline val Activity.contentView: View?
+ get() = (findViewById(android.R.id.content) as? ViewGroup)?.getChildAt(0)
+
inline fun Activity.snackbar(
text: String,
duration: Int = Snackbar.LENGTH_LONG,
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt
index 314ca60..5b4b188 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt
@@ -20,10 +20,12 @@ import android.app.Activity
import android.app.ActivityOptions
import android.content.Context
import android.os.Bundle
+import android.os.Parcelable
import android.util.Pair
import android.view.View
import androidx.annotation.AnimRes
import ca.allanwang.kau.R
+import java.io.Serializable
/**
* Created by Allan Wang on 10/12/17.
@@ -37,6 +39,56 @@ infix fun Bundle.with(bundle: Bundle?): Bundle {
}
/**
+ * Saves all bundle args based on their respective types.
+ *
+ * Taken courtesy of <a href="https://github.com/Kotlin/anko">Anko</a>
+ *
+ * Previously, Anko was a dependency in KAU, but has been removed on 12/24/2018
+ * as most of the methods weren't used
+ */
+fun bundleOf(vararg params: kotlin.Pair<String, Any?>): Bundle {
+ val b = Bundle()
+ for (p in params) {
+ val (k, v) = p
+ when (v) {
+ null -> b.putSerializable(k, null)
+ is Boolean -> b.putBoolean(k, v)
+ is Byte -> b.putByte(k, v)
+ is Char -> b.putChar(k, v)
+ is Short -> b.putShort(k, v)
+ is Int -> b.putInt(k, v)
+ is Long -> b.putLong(k, v)
+ is Float -> b.putFloat(k, v)
+ is Double -> b.putDouble(k, v)
+ is String -> b.putString(k, v)
+ is CharSequence -> b.putCharSequence(k, v)
+ is Parcelable -> b.putParcelable(k, v)
+ is Serializable -> b.putSerializable(k, v)
+ is BooleanArray -> b.putBooleanArray(k, v)
+ is ByteArray -> b.putByteArray(k, v)
+ is CharArray -> b.putCharArray(k, v)
+ is DoubleArray -> b.putDoubleArray(k, v)
+ is FloatArray -> b.putFloatArray(k, v)
+ is IntArray -> b.putIntArray(k, v)
+ is LongArray -> b.putLongArray(k, v)
+ is Array<*> -> {
+ @Suppress("UNCHECKED_CAST")
+ when {
+ v.isArrayOf<Parcelable>() -> b.putParcelableArray(k, v as Array<out Parcelable>)
+ v.isArrayOf<CharSequence>() -> b.putCharSequenceArray(k, v as Array<out CharSequence>)
+ v.isArrayOf<String>() -> b.putStringArray(k, v as Array<out String>)
+ else -> throw KauException("Unsupported bundle component (${v.javaClass})")
+ }
+ }
+ is ShortArray -> b.putShortArray(k, v)
+ is Bundle -> b.putBundle(k, v)
+ else -> throw KauException("Unsupported bundle component (${v.javaClass})")
+ }
+ }
+ return b
+}
+
+/**
* Adds transition bundle if context is activity and build is lollipop+
*/
@SuppressLint("NewApi")
@@ -62,7 +114,8 @@ fun Bundle.withSceneTransitionAnimation(parent: View, data: Map<Int, String>) =
@SuppressLint("NewApi")
fun Bundle.withSceneTransitionAnimation(context: Context, data: Map<View, String>) {
if (context !is Activity || !buildIsLollipopAndUp) return
- val options = ActivityOptions.makeSceneTransitionAnimation(context,
+ val options = ActivityOptions.makeSceneTransitionAnimation(
+ context,
*data.map { (view, tag) -> Pair(view, tag) }.toTypedArray()
)
putAll(options.toBundle())
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 134126d..60ef236 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2018 Allan Wang
+ * Copyright 2017 Allan Wang
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,6 +26,8 @@ import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
import android.util.TypedValue
import android.view.View
import android.view.animation.AnimationUtils
@@ -45,10 +47,33 @@ import androidx.core.content.ContextCompat
import ca.allanwang.kau.R
import ca.allanwang.kau.logging.KL
import com.afollestad.materialdialogs.MaterialDialog
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.GlobalScope
+import kotlin.coroutines.CoroutineContext
/**
* Created by Allan Wang on 2017-06-03.
*/
+private object ContextHelper: CoroutineScope {
+
+ val handler = Handler(Looper.getMainLooper())
+
+ override val coroutineContext: CoroutineContext
+ get() = Dispatchers.Main
+}
+
+/**
+ * Most context items implement [CoroutineScope] by default.
+ * 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
+ get() = this as? CoroutineScope ?: ContextHelper
+
+fun Context.runOnUiThread(f: Context.() -> Unit) {
+ if (Looper.getMainLooper() === Looper.myLooper()) f() else ContextHelper.handler.post { f() }
+}
/**
* Helper class to launch an activity from a context
diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt
index 1c97900..75dc1c1 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt
@@ -16,12 +16,11 @@
package ca.allanwang.kau.utils
import androidx.fragment.app.Fragment
-import org.jetbrains.anko.bundleOf
/**
* Created by Allan Wang on 2017-07-02.
*/
-fun <T : Fragment> T.withArguments(vararg params: Pair<String, Any>): T {
+fun <T : Fragment> T.withArguments(vararg params: Pair<String, Any?>): T {
arguments = bundleOf(*params)
return this
}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt b/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt
index 6a75aa9..51e63f9 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt
@@ -26,11 +26,12 @@ import androidx.annotation.LayoutRes
import androidx.annotation.XmlRes
import androidx.recyclerview.widget.RecyclerView
import ca.allanwang.kau.R
+import ca.allanwang.kau.utils.ctxCoroutine
import ca.allanwang.kau.utils.materialDialog
import ca.allanwang.kau.utils.use
import com.afollestad.materialdialogs.MaterialDialog
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
import org.xmlpull.v1.XmlPullParser
/**
@@ -39,15 +40,13 @@ import org.xmlpull.v1.XmlPullParser
* Easy changelog loader
*/
fun Context.showChangelog(@XmlRes xmlRes: Int, @ColorInt textColor: Int? = null, customize: MaterialDialog.Builder.() -> Unit = {}) {
- doAsync {
- val items = parse(this@showChangelog, xmlRes)
- uiThread {
- materialDialog {
- title(R.string.kau_changelog)
- positiveText(R.string.kau_great)
- adapter(ChangelogAdapter(items, textColor), null)
- customize()
- }
+ ctxCoroutine.launch {
+ val items = async { parse(this@showChangelog, xmlRes) }.await()
+ materialDialog {
+ title(R.string.kau_changelog)
+ positiveText(R.string.kau_great)
+ adapter(ChangelogAdapter(items, textColor), null)
+ customize()
}
}
}
diff --git a/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt b/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
index cb57216..73d7d6c 100644
--- a/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
+++ b/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt
@@ -21,8 +21,6 @@ import android.text.Html
import android.text.Spanned
import androidx.annotation.XmlRes
import ca.allanwang.kau.utils.use
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
import org.xmlpull.v1.XmlPullParser
/**
@@ -30,8 +28,8 @@ import org.xmlpull.v1.XmlPullParser
*/
/**
- * Parse an xml asynchronously with two tags, <question>Text</question> and <answer>Text</answer>,
- * and invoke the [callback] on the ui thread
+ * Parse an xml asynchronously with two tags, <question>Text</question> and <answer>Text</answer>.
+ * Note that this should executed in a background thread.
*/
@Suppress("DEPRECATION")
fun Context.kauParseFaq(
@@ -39,47 +37,44 @@ fun Context.kauParseFaq(
/**
* If \n is used, it will automatically be converted to </br>
*/
- parseNewLine: Boolean = true,
- callback: (items: List<FaqItem>) -> Unit
-) {
- doAsync {
- val items = mutableListOf<FaqItem>()
- resources.getXml(xmlRes).use { parser: XmlResourceParser ->
- var eventType = parser.eventType
- var question: Spanned? = null
- var flag = -1 //-1, 0, 1 -> invalid, question, answer
- while (eventType != XmlPullParser.END_DOCUMENT) {
- if (eventType == XmlPullParser.START_TAG) {
- flag = when (parser.name) {
- "question" -> 0
- "answer" -> 1
- else -> -1
+ parseNewLine: Boolean = true
+): List<FaqItem> {
+ val items = mutableListOf<FaqItem>()
+ resources.getXml(xmlRes).use { parser: XmlResourceParser ->
+ var eventType = parser.eventType
+ var question: Spanned? = null
+ var flag = -1 //-1, 0, 1 -> invalid, question, answer
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ flag = when (parser.name) {
+ "question" -> 0
+ "answer" -> 1
+ else -> -1
+ }
+ } else if (eventType == XmlPullParser.TEXT) {
+ when (flag) {
+ 0 -> {
+ question = Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))
+ flag = -1
}
- } else if (eventType == XmlPullParser.TEXT) {
- when (flag) {
- 0 -> {
- question = Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))
- flag = -1
- }
- 1 -> {
- items.add(
- FaqItem(
- items.size + 1,
- question
- ?: throw IllegalArgumentException("KAU FAQ answer found without a question"),
- Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))
- )
+ 1 -> {
+ items.add(
+ FaqItem(
+ items.size + 1,
+ question
+ ?: throw IllegalArgumentException("KAU FAQ answer found without a question"),
+ Html.fromHtml(parser.text.replace("\n", if (parseNewLine) "<br/>" else ""))
)
- question = null
- flag = -1
- }
+ )
+ question = null
+ flag = -1
}
}
- eventType = parser.next()
}
+ eventType = parser.next()
}
- uiThread { callback(items) }
}
+ return items
}
data class FaqItem(val number: Int, val question: Spanned, val answer: Spanned)
diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt
deleted file mode 100644
index 1fbc38f..0000000
--- a/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.kotlin
-
-import org.jetbrains.anko.doAsync
-import org.junit.Test
-import java.util.Random
-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<Int> ->
- 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)
- }
-}
diff --git a/docs/Changelog.md b/docs/Changelog.md
index 1e1b0d8..283759d 100644
--- a/docs/Changelog.md
+++ b/docs/Changelog.md
@@ -1,5 +1,11 @@
# Changelog
+## v4.0.0-alpha02
+* :core: Remove anko dependency. Methods that used it now use coroutines; see the migration doc for minor changes
+* :core: Add default CoroutineScope implementation to KauBaseActivity
+* :core: Remove zip class. Coroutines and join can be used as an alternative
+* :mediapicker: Use video preloading instead of full async loading
+
## v4.0.0-alpha01
* Migrate to androidx. See migration for external dependency changes.
* :core: Remove deprecation warning for Kotterknife
diff --git a/docs/Migration.md b/docs/Migration.md
index 8c5e016..cdaca4c 100644
--- a/docs/Migration.md
+++ b/docs/Migration.md
@@ -2,6 +2,19 @@
Below are some highlights on major refactoring/breaking changes
+# v4.0.1-alpha02
+
+* `kauParseFaq` is now synchronous.
+
+## Anko has been removed
+
+A lot of the methods are already implemented in KAU, and it was primarily imported for its `doAsync` methods. Now, they have been replaced with coroutines.
+Some methods have been copied over:
+
+* import org.jetbrains.anko.runOnUiThread > import ca.allanwang.kau.utils.runOnUiThread
+* import org.jetbrains.anko.contentView > import ca.allanwang.kau.utils.contentView
+* import org.jetbrains.anko.bundleOf > import ca.allanwang.kau.utils.bundleOf
+
# v4.0.0-alpha01
This is the first introduction of androidx. The goal is to just do a migration with minimal changes.
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 02b6e98..450bc6e 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
@@ -33,8 +33,8 @@ import ca.allanwang.kau.utils.statusBarColor
import ca.allanwang.kau.utils.withLinearAdapter
import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
import kotlinx.android.synthetic.main.kau_pref_activity.*
-import org.jetbrains.anko.doAsync
-import org.jetbrains.anko.uiThread
+import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
import java.util.Stack
abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract {
@@ -104,19 +104,24 @@ abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract {
builder: KPrefAdapterBuilder.() -> Unit,
first: Boolean
) {
- doAsync {
- val items = KPrefAdapterBuilder(globalOptions)
- builder(items)
- kprefStack.push(toolbarTitleRes to items.list)
+ launch {
+ val items = async {
+ val items = KPrefAdapterBuilder(globalOptions)
+ builder(items)
+ kprefStack.push(toolbarTitleRes to items.list)
+ items.list
+ }.await()
kau_recycler.itemAnimator = if (animate && !first) recyclerAnimatorNext else null
- uiThread {
- adapter.clear()
- adapter.add(items.list.filter { it.core.visible() })
- toolbar.setTitle(toolbarTitleRes)
- }
+ show(toolbarTitleRes, items)
}
}
+ private fun show(@StringRes toolbarTitleRes: Int, items: List<KPrefItemCore>) {
+ toolbar.setTitle(toolbarTitleRes)
+ adapter.clear()
+ adapter.add(items.filter { it.core.visible() })
+ }
+
/**
* Pops the stack and loads the next kpref list
* Indices are not checked so ensure that this is possible first
@@ -125,9 +130,7 @@ abstract class KPrefActivity : KauBaseActivity(), KPrefActivityContract {
kprefStack.pop()
val (title, list) = kprefStack.peek()
kau_recycler.itemAnimator = if (animate) recyclerAnimatorPrev else null
- adapter.clear()
- adapter.add(list.filter { it.core.visible() })
- toolbar.setTitle(title)
+ show(title, list)
}
/**
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 abdc266..7004967 100644
--- a/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt
+++ b/mediapicker/src/main/kotlin/ca/allanwang/kau/mediapicker/MediaPickerCore.kt
@@ -49,10 +49,8 @@ import com.mikepenz.fastadapter.adapters.ItemAdapter
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.IconicsDrawable
import com.mikepenz.iconics.typeface.IIcon
-import org.jetbrains.anko.doAsync
+import kotlinx.coroutines.CancellationException
import java.io.File
-import java.util.concurrent.ExecutionException
-import java.util.concurrent.Future
/**
* Created by Allan Wang on 2017-07-23.
@@ -121,7 +119,6 @@ abstract class MediaPickerCore<T : IItem<*, *>>(
lateinit var glide: RequestManager
private var hasPreloaded = false
- private var prefetcher: Future<*>? = null
val adapter = ItemAdapter<T>()
@@ -137,7 +134,7 @@ abstract class MediaPickerCore<T : IItem<*, *>>(
fun initializeRecycler(recycler: RecyclerView) {
val adapterHeader = ItemAdapter<MediaActionItem>()
- val fulladapter = fastAdapter(adapterHeader, adapter)
+ val fulladapter = fastAdapter<IItem<*, *>>(adapterHeader, adapter)
adapterHeader.add(mediaActions.map { MediaActionItem(it, mediaType) })
recycler.apply {
val manager = object : GridLayoutManager(context, computeColumnCount(context)) {
@@ -146,7 +143,6 @@ abstract class MediaPickerCore<T : IItem<*, *>>(
}
}
setItemViewCacheSize(CACHE_SIZE)
- isDrawingCacheEnabled = true
layoutManager = manager
adapter = fulladapter
setHasFixedSize(true)
@@ -195,18 +191,14 @@ abstract class MediaPickerCore<T : IItem<*, *>>(
addItems(models.map { converter(it) })
if (!hasPreloaded && mediaType == MediaType.VIDEO) {
hasPreloaded = true
- prefetcher = doAsync {
- models.subList(0, Math.min(models.size, 50)).map { it.data }.forEach {
- val target = glide.load(it)
- .applyMediaOptions(this@MediaPickerCore)
- .submit()
- try {
- target.get()
- } catch (ignored: InterruptedException) {
- } catch (ignored: ExecutionException) {
- } finally {
- glide.clear(target)
- }
+ val preloads = models.subList(0, Math.min(models.size, 50)).map {
+ glide.load(it.data)
+ .applyMediaOptions(this@MediaPickerCore)
+ .preload()
+ }
+ job.invokeOnCompletion {
+ if (it is CancellationException) {
+ preloads.forEach(glide::clear)
}
}
}
@@ -242,11 +234,6 @@ abstract class MediaPickerCore<T : IItem<*, *>>(
open fun onStatusChange(loaded: Boolean) {}
- override fun onDestroy() {
- prefetcher?.cancel(true)
- super.onDestroy()
- }
-
/**
* Method used to retrieve uri data for API 19+
* See <a href="http://hmkcode.com/android-display-selected-image-and-its-real-path/"></a>
diff --git a/sample/src/main/res/xml/kau_changelog.xml b/sample/src/main/res/xml/kau_changelog.xml
index 6c0fea1..3c49078 100644
--- a/sample/src/main/res/xml/kau_changelog.xml
+++ b/sample/src/main/res/xml/kau_changelog.xml
@@ -6,6 +6,16 @@
<item text="" />
-->
+ <version title="v4.0.0-alpha02" />
+ <item text=":core: Remove anko dependency. Methods that used it now use coroutines; see the migration doc for minor changes" />
+ <item text=":core: Add default CoroutineScope implementation to KauBaseActivity" />
+ <item text=":core: Remove zip class. Coroutines and join can be used as an alternative" />
+ <item text=":mediapicker: Use video preloading instead of full async loading" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+ <item text="" />
+
<version title="v4.0.0-alpha01" />
<item text="Migrate to androidx. See migration for external dependency changes." />
<item text=":core: Remove deprecation warning for Kotterknife" />
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 11880cd..169e914 100644
--- a/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt
+++ b/searchview/src/main/kotlin/ca/allanwang/kau/searchview/SearchView.kt
@@ -53,6 +53,7 @@ import ca.allanwang.kau.utils.hideKeyboard
import ca.allanwang.kau.utils.invisibleIf
import ca.allanwang.kau.utils.isVisible
import ca.allanwang.kau.utils.parentViewGroup
+import ca.allanwang.kau.utils.runOnUiThread
import ca.allanwang.kau.utils.setIcon
import ca.allanwang.kau.utils.setMarginTop
import ca.allanwang.kau.utils.showKeyboard
@@ -65,7 +66,6 @@ import com.mikepenz.fastadapter.commons.adapters.FastItemAdapter
import com.mikepenz.google_material_typeface_library.GoogleMaterial
import com.mikepenz.iconics.typeface.IIcon
import kotlinx.android.synthetic.main.kau_search_view.view.*
-import org.jetbrains.anko.runOnUiThread
/**
* Created by Allan Wang on 2017-06-23.