diff options
author | Allan Wang <me@allanwang.ca> | 2019-06-07 12:41:00 -0400 |
---|---|---|
committer | Allan Wang <me@allanwang.ca> | 2019-06-07 12:41:00 -0400 |
commit | 5d86d9089697b152192b2786fbe0c708dd8b5e2b (patch) | |
tree | 99b563b8c234330d2bbbc0145f086d8691ee9376 /core/src/main/kotlin/ca | |
parent | 572d470a2677eec0405a7b16ab9a2cfb954d6832 (diff) | |
parent | 879ac366074697dd0a7fbb2c3d99a48d7aeeb22d (diff) | |
download | kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.tar.gz kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.tar.bz2 kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.zip |
Merge dev
Diffstat (limited to 'core/src/main/kotlin/ca')
52 files changed, 1594 insertions, 785 deletions
diff --git a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt index 2b9d91c..78661b1 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/email/EmailBuilder.kt @@ -1,3 +1,18 @@ +/* + * 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.email import android.app.Activity @@ -6,12 +21,15 @@ import android.content.Intent import android.content.pm.PackageManager import android.net.Uri import android.os.Build -import android.support.annotation.StringRes import android.util.DisplayMetrics +import androidx.annotation.StringRes import ca.allanwang.kau.R import ca.allanwang.kau.logging.KL -import ca.allanwang.kau.utils.* - +import ca.allanwang.kau.utils.boolean +import ca.allanwang.kau.utils.installerPackageName +import ca.allanwang.kau.utils.isAppInstalled +import ca.allanwang.kau.utils.string +import ca.allanwang.kau.utils.toast /** * Created by Allan Wang on 2017-06-20. @@ -28,7 +46,7 @@ class EmailBuilder(val email: String, val subject: String) { private var attachment: Uri? = null fun checkPackage(packageName: String, appName: String) = - packages.add(Package(packageName, appName)) + packages.add(Package(packageName, appName)) fun addItem(key: String, value: String) = pairs.put(key, value) @@ -45,24 +63,24 @@ class EmailBuilder(val email: String, val subject: String) { fun getIntent(context: Context): Intent { val intent = Intent(Intent.ACTION_SEND) - .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - .putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) - .putExtra(Intent.EXTRA_SUBJECT, subject) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .putExtra(Intent.EXTRA_EMAIL, arrayOf(email)) + .putExtra(Intent.EXTRA_SUBJECT, subject) val emailBuilder = StringBuilder() emailBuilder.append(message).append("\n\n") if (deviceDetails) { - val deviceItems = linkedMapOf( - "OS Version" to "${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})", - "OS SDK" to Build.VERSION.SDK_INT, - "Device (Manufacturer)" to "${Build.DEVICE} (${Build.MANUFACTURER})", - "Model (Product)" to "${Build.MODEL} (${Build.PRODUCT})", - "Package Installer" to (context.installerPackageName ?: "None"), - "Tablet" to context.boolean(R.bool.kau_is_tablet) + val deviceItems = mutableListOf( + "OS Version" to "${System.getProperty("os.version")} (${Build.VERSION.INCREMENTAL})", + "OS SDK" to Build.VERSION.SDK_INT, + "Device (Manufacturer)" to "${Build.DEVICE} (${Build.MANUFACTURER})", + "Model (Product)" to "${Build.MODEL} (${Build.PRODUCT})", + "Package Installer" to (context.installerPackageName ?: "None"), + "Tablet" to context.boolean(R.bool.kau_is_tablet) ) if (context is Activity) { val metric = DisplayMetrics() context.windowManager.defaultDisplay.getMetrics(metric) - deviceItems["Screen Dimensions"] = "${metric.widthPixels} x ${metric.heightPixels}" + deviceItems.add("Screen Dimensions" to "${metric.widthPixels} x ${metric.heightPixels}") } deviceItems.forEach { (k, v) -> emailBuilder.append("$k: $v\n") } } @@ -76,8 +94,8 @@ class EmailBuilder(val email: String, val subject: String) { appInfo.versionCode.toString() } emailBuilder.append("\nApp: ").append(context.packageName) - .append("\nApp Version Name: ").append(appInfo.versionName) - .append("\nApp Version Code: ").append(versionCode).append("\n") + .append("\nApp Version Name: ").append(appInfo.versionName) + .append("\nApp Version Code: ").append(versionCode).append("\n") } catch (e: PackageManager.NameNotFoundException) { KL.e { "EmailBuilder packageInfo not found" } } @@ -112,7 +130,7 @@ class EmailBuilder(val email: String, val subject: String) { val intent = getIntent(context) intent.extras() val packageName = intent.resolveActivity(context.packageManager)?.packageName - ?: return context.toast(R.string.kau_error_no_email, log = true) + ?: return context.toast(R.string.kau_error_no_email, log = true) val attachment = this.attachment if (attachment != null) { @@ -124,12 +142,12 @@ class EmailBuilder(val email: String, val subject: String) { } } -fun Context.sendEmail(@StringRes emailId: Int, @StringRes subjectId: Int, builder: EmailBuilder.() -> Unit = {}) = sendEmail(string(emailId), string(subjectId), builder) - +fun Context.sendEmail(@StringRes emailId: Int, @StringRes subjectId: Int, builder: EmailBuilder.() -> Unit = {}) = + sendEmail(string(emailId), string(subjectId), builder) fun Context.sendEmail(email: String, subject: String, builder: EmailBuilder.() -> Unit = {}) { EmailBuilder(email, subject).apply { builder() execute(this@sendEmail) } -}
\ No newline at end of file +} 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 87d94ce..cb8f4bf 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/internal/KauBaseActivity.kt @@ -1,7 +1,28 @@ +/* + * 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.internal -import android.support.v7.app.AppCompatActivity +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity import ca.allanwang.kau.permissions.kauOnRequestPermissionsResult +import ca.allanwang.kau.utils.ContextHelper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlin.coroutines.CoroutineContext /** * Created by Allan Wang on 2017-08-01. @@ -11,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() = ContextHelper.dispatcher + 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/Debouncer.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt index de5d148..ea13f73 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Debouncer.kt @@ -1,3 +1,18 @@ +/* + * 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 ca.allanwang.kau.logging.KL @@ -56,7 +71,6 @@ open class Debouncer(var interval: Long) { task = null } } - } /* diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/FlyWeight.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/FlyWeight.kt deleted file mode 100644 index 03e98d2..0000000 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/FlyWeight.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ca.allanwang.kau.kotlin - -/** - * Created by Allan Wang on 26/12/17. - * - * Kotlin implementation of a flyweight design pattern - * Values inside the map will be returned directly - * Otherwise, it will be created with the supplier, saved, and returned - * In the event a default is provided, the default takes precedence - */ -fun <K : Any, V : Any> flyweight(creator: (K) -> V) = FlyWeight(creator) - -class FlyWeight<K : Any, V : Any>(private val creator: (key: K) -> V) : Map<K, V> { - - private val map = mutableMapOf<K, V>() - - override val entries: Set<Map.Entry<K, V>> - get() = map.entries - override val keys: Set<K> - get() = map.keys - override val size: Int - get() = map.size - override val values: Collection<V> - get() = map.values - - override fun containsKey(key: K) = map.containsKey(key) - - override fun containsValue(value: V) = map.containsValue(value) - - override fun getOrDefault(key: K, defaultValue: V) = map.getOrDefault(key, defaultValue) - - override fun isEmpty() = map.isEmpty() - - override fun get(key: K): V { - if (!map.containsKey(key)) - map.put(key, creator(key)) - return map[key]!! - } - -}
\ No newline at end of file diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt index 0a45b65..e2a59d7 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyContext.kt @@ -1,11 +1,26 @@ +/* + * 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 android.content.Context -import android.support.annotation.AnimRes -import android.support.annotation.InterpolatorRes import android.view.animation.Animation import android.view.animation.AnimationUtils import android.view.animation.Interpolator +import androidx.annotation.AnimRes +import androidx.annotation.InterpolatorRes /** * Created by Allan Wang on 2017-05-30. @@ -47,5 +62,4 @@ class LazyContext<out T>(private val initializer: (context: Context) -> T, lock: } } } - -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt index 979f7a7..ee38d48 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/LazyResettable.kt @@ -1,3 +1,18 @@ +/* + * 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 java.io.Serializable @@ -80,4 +95,4 @@ class LazyResettableRegistry : ILazyResettableRegistry { override fun invalidateAll() = lazyRegistry.forEach { it.invalidate() } override fun clear() = lazyRegistry.clear() -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Streams.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Streams.kt index 303153f..65d15a7 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Streams.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Streams.kt @@ -1,3 +1,18 @@ +/* + * 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 /** @@ -9,7 +24,6 @@ package ca.allanwang.kau.kotlin * Since we don't have access to the internals of our extended class, * We will simply iterate and remove when the filter returns {@code false} */ -@Synchronized inline fun <T, C : MutableIterable<T>> C.kauRemoveIf(filter: (item: T) -> Boolean): C { val iter = iterator() while (iter.hasNext()) @@ -27,4 +41,4 @@ inline fun <T : Any> Iterator<T>.firstOrNull(predicate: (T) -> Boolean): T? { if (predicate(data)) return data } return null -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Utils.kt b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Utils.kt index 1ba1de6..1e35ecf 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Utils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kotlin/Utils.kt @@ -1,6 +1,21 @@ +/* + * 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 /** * Created by Allan Wang on 07/04/18. */ -inline fun <reified T : Any> javaClass(): Class<T> = T::class.java
\ No newline at end of file +inline fun <reified T : Any> javaClass(): Class<T> = T::class.java 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 17ae5e5..0000000 --- a/core/src/main/kotlin/ca/allanwang/kau/kotlin/Zip.kt +++ /dev/null @@ -1,89 +0,0 @@ -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/kpref/KPref.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt index 12f9606..7a6330f 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPref.kt @@ -1,3 +1,18 @@ +/* + * 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.kpref import android.content.Context @@ -47,5 +62,4 @@ open class KPref { operator fun get(key: String): ILazyResettable<*>? = prefMap[key] open fun deleteKeys(): Array<String> = arrayOf() - -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt index b80479e..9813f24 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefDelegate.kt @@ -1,31 +1,47 @@ +/* + * 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.kpref import ca.allanwang.kau.kotlin.ILazyResettable - fun KPref.kpref(key: String, fallback: Boolean, postSetter: (value: Boolean) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefBooleanTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefBooleanTransaction, postSetter) fun KPref.kpref(key: String, fallback: Float, postSetter: (value: Float) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefFloatTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefFloatTransaction, postSetter) -@Deprecated("Double is not supported in SharedPreferences; cast to float yourself", - ReplaceWith("kpref(key, fallback.toFloat(), postSetter)"), - DeprecationLevel.WARNING) +@Deprecated( + "Double is not supported in SharedPreferences; cast to float yourself", + ReplaceWith("kpref(key, fallback.toFloat(), postSetter)"), + DeprecationLevel.WARNING +) fun KPref.kpref(key: String, fallback: Double, postSetter: (value: Float) -> Unit = {}) = - kpref(key, fallback.toFloat(), postSetter) + kpref(key, fallback.toFloat(), postSetter) fun KPref.kpref(key: String, fallback: Int, postSetter: (value: Int) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefIntTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefIntTransaction, postSetter) fun KPref.kpref(key: String, fallback: Long, postSetter: (value: Long) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefLongTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefLongTransaction, postSetter) fun KPref.kpref(key: String, fallback: Set<String>, postSetter: (value: Set<String>) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefSetTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefSetTransaction, postSetter) fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Unit = {}) = - KPrefDelegate(key, fallback, this, KPrefStringTransaction, postSetter) + KPrefDelegate(key, fallback, this, KPrefStringTransaction, postSetter) /** * Created by Allan Wang on 2017-06-07. @@ -35,11 +51,11 @@ fun KPref.kpref(key: String, fallback: String, postSetter: (value: String) -> Un * Also contains an optional mutable postSetter that will be called every time a new value is given */ class KPrefDelegate<T> internal constructor( - private val key: String, - private val fallback: T, - private val pref: KPref, - private val transaction: KPrefTransaction<T>, - private var postSetter: (value: T) -> Unit = {} + private val key: String, + private val fallback: T, + private val pref: KPref, + private val transaction: KPrefTransaction<T>, + private var postSetter: (value: T) -> Unit = {} ) : ILazyResettable<T> { private object UNINITIALIZED @@ -91,4 +107,4 @@ class KPrefDelegate<T> internal constructor( } } -class KPrefException(message: String) : IllegalAccessException(message)
\ No newline at end of file +class KPrefException(message: String) : IllegalAccessException(message) diff --git a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt index ed30a44..204e07e 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefSingleDelegate.kt @@ -1,3 +1,18 @@ +/* + * 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.kpref import ca.allanwang.kau.kotlin.ILazyResettable @@ -12,7 +27,8 @@ fun KPref.kprefSingle(key: String) = KPrefSingleDelegate(key, this) * All subsequent retrievals will be [false] * This is useful for one time toggles such as showcasing items */ -class KPrefSingleDelegate internal constructor(private val key: String, private val pref: KPref, lock: Any? = null) : ILazyResettable<Boolean> { +class KPrefSingleDelegate internal constructor(private val key: String, private val pref: KPref, lock: Any? = null) : + ILazyResettable<Boolean> { @Volatile private var _value: Boolean? = null @@ -52,5 +68,4 @@ class KPrefSingleDelegate internal constructor(private val key: String, private override fun isInitialized(): Boolean = _value != null override fun toString(): String = if (isInitialized()) value.toString() else "Lazy kPref $key not initialized yet." - -}
\ No newline at end of file +} 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 773d208..1070e11 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/kpref/KPrefTransaction.kt @@ -1,3 +1,18 @@ +/* + * 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.kpref import android.content.SharedPreferences @@ -9,7 +24,7 @@ internal interface KPrefTransaction<T> { internal object KPrefBooleanTransaction : KPrefTransaction<Boolean> { override fun get(prefs: SharedPreferences, key: String, fallback: Boolean) = - prefs.getBoolean(key, fallback) + prefs.getBoolean(key, fallback) override fun set(editor: SharedPreferences.Editor, key: String, data: Boolean) { editor.putBoolean(key, data) @@ -18,7 +33,7 @@ internal object KPrefBooleanTransaction : KPrefTransaction<Boolean> { internal object KPrefIntTransaction : KPrefTransaction<Int> { override fun get(prefs: SharedPreferences, key: String, fallback: Int) = - prefs.getInt(key, fallback) + prefs.getInt(key, fallback) override fun set(editor: SharedPreferences.Editor, key: String, data: Int) { editor.putInt(key, data) @@ -27,7 +42,7 @@ internal object KPrefIntTransaction : KPrefTransaction<Int> { internal object KPrefLongTransaction : KPrefTransaction<Long> { override fun get(prefs: SharedPreferences, key: String, fallback: Long) = - prefs.getLong(key, fallback) + prefs.getLong(key, fallback) override fun set(editor: SharedPreferences.Editor, key: String, data: Long) { editor.putLong(key, data) @@ -36,7 +51,7 @@ internal object KPrefLongTransaction : KPrefTransaction<Long> { internal object KPrefFloatTransaction : KPrefTransaction<Float> { override fun get(prefs: SharedPreferences, key: String, fallback: Float) = - prefs.getFloat(key, fallback) + prefs.getFloat(key, fallback) override fun set(editor: SharedPreferences.Editor, key: String, data: Float) { editor.putFloat(key, data) @@ -45,7 +60,7 @@ internal object KPrefFloatTransaction : KPrefTransaction<Float> { internal object KPrefStringTransaction : KPrefTransaction<String> { 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) @@ -54,11 +69,9 @@ internal object KPrefStringTransaction : KPrefTransaction<String> { internal object KPrefSetTransaction : KPrefTransaction<Set<String>> { override fun get(prefs: SharedPreferences, key: String, fallback: Set<String>) = - prefs.getStringSet(key, fallback)!! + prefs.getStringSet(key, fallback)!! override fun set(editor: SharedPreferences.Editor, key: String, data: Set<String>) { editor.putStringSet(key, data) } } - - 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 9f48ab5..9fbc070 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/logging/KL.kt @@ -1,3 +1,18 @@ +/* + * 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.logging import ca.allanwang.kau.BuildConfig @@ -7,4 +22,13 @@ import ca.allanwang.kau.BuildConfig * * Internal KAU logger */ -object KL : KauLogger("KAU", { BuildConfig.DEBUG })
\ No newline at end of file +object KL : KauLogger("KAU", { BuildConfig.DEBUG }) { + + /** + * Logger with searchable tag and thread info + */ + inline fun test(message: () -> Any?) { + if (BuildConfig.DEBUG) + d { "Test1234 ${Thread.currentThread().name} ${message()}" } + } +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt b/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt index 799d32f..4562b00 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/logging/KauLogger.kt @@ -1,9 +1,24 @@ +/* + * 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. + */ @file:Suppress("NOTHING_TO_INLINE") package ca.allanwang.kau.logging -import android.os.Looper import android.util.Log +import ca.allanwang.kau.utils.kauIsMainThread /** * Created by Allan Wang on 2017-05-28. @@ -25,15 +40,15 @@ import android.util.Log * for production builds */ open class KauLogger( - /** - * Tag to be used for each log - */ - val tag: String, - /** - * Toggle to dictate whether a message should be logged - */ - var shouldLog: (priority: Int) -> Boolean = { true }) { - + /** + * Tag to be used for each log + */ + val tag: String, + /** + * Toggle to dictate whether a message should be logged + */ + var shouldLog: (priority: Int) -> Boolean = { true } +) { inline fun v(message: () -> Any?) = log(Log.VERBOSE, message) @@ -67,7 +82,7 @@ open class KauLogger( inline fun checkThread(id: Int) { d { val name = Thread.currentThread().name - val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" + val status = if (kauIsMainThread) "is" else "is not" "$id $status in the main thread - thread name: $name" } } @@ -96,16 +111,16 @@ class KauLoggerExtension(val tag: String, val logger: KauLogger) { } inline fun log(priority: Int, message: () -> Any?, t: Throwable? = null) = - logger.log(priority, { - val msg = message()?.toString() - if (msg == null) null - else "$tag: $msg" - }, t) + logger.log(priority, { + val msg = message()?.toString() + if (msg == null) null + else "$tag: $msg" + }, t) inline fun checkThread(id: Int) { d { val name = Thread.currentThread().name - val status = if (Looper.myLooper() == Looper.getMainLooper()) "is" else "is not" + val status = if (kauIsMainThread) "is" else "is not" "$id $status in the main thread - thread name: $name" } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt index 36456ec..96adfa3 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionManager.kt @@ -1,9 +1,25 @@ +/* + * 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.permissions import android.app.Activity import android.content.Context import android.content.pm.PackageManager -import android.support.v4.app.ActivityCompat +import androidx.core.app.ActivityCompat +import ca.allanwang.kau.kotlin.kauRemoveIf import ca.allanwang.kau.kotlin.lazyContext import ca.allanwang.kau.logging.KL import ca.allanwang.kau.utils.KauException @@ -12,43 +28,55 @@ import ca.allanwang.kau.utils.hasPermission import ca.allanwang.kau.utils.toast import java.lang.ref.WeakReference - /** * Created by Allan Wang on 2017-07-03. * - * Permission manager that is decoupled from activities - * Keeps track of pending requests, and warns about invalid requests + * Permission manager that is decoupled from activities. + * Keeps track of pending requests, and warns about invalid requests. */ internal object PermissionManager { - private var requestInProgress = false - private val pendingResults: MutableList<WeakReference<PermissionResult>> by lazy { mutableListOf<WeakReference<PermissionResult>>() } + private val pendingResults = mutableListOf<WeakReference<PermissionResult>>() /** * Retrieve permissions requested in our manifest */ - private val manifestPermission = lazyContext<Array<String>> { + private val manifestPermission = lazyContext<Set<String>> { try { - it.packageManager.getPackageInfo(it.packageName, PackageManager.GET_PERMISSIONS)?.requestedPermissions - ?: emptyArray() + it.packageManager.getPackageInfo( + it.packageName, + PackageManager.GET_PERMISSIONS + )?.requestedPermissions?.toSet() + ?: emptySet() } catch (e: Exception) { - emptyArray() + emptySet() } } - operator fun invoke(context: Context, permissions: Array<out String>, callback: (granted: Boolean, deniedPerm: String?) -> Unit) { + /** + * Registers a new permission request. + * It is expected that the callback will be called eventually, unless the parent activity is destroyed. + */ + operator fun invoke( + context: Context, + permissions: Array<out String>, + callback: (granted: Boolean, deniedPerm: String?) -> Unit + ) { KL.d { "Permission manager for: ${permissions.contentToString()}" } if (!buildIsMarshmallowAndUp) return callback(true, null) val missingPermissions = permissions.filter { !context.hasPermission(it) } if (missingPermissions.isEmpty()) return callback(true, null) pendingResults.add(WeakReference(PermissionResult(permissions, callback = callback))) - if (!requestInProgress) { - requestInProgress = true + if (pendingResults.size == 1) { requestPermissions(context, missingPermissions.toTypedArray()) - } else KL.d { "Request is postponed since another one is still in progress; did you remember to override onRequestPermissionsResult?" } + } else { + KL.d { "Request is postponed since another one is still in progress; did you remember to override onRequestPermissionsResult?" } + } } - @Synchronized + /** + * Checks that the listed permissions can be requested, and submits the request to the provided activity + */ private fun requestPermissions(context: Context, permissions: Array<out String>) { permissions.forEach { if (!manifestPermission(context).contains(it)) { @@ -58,36 +86,27 @@ internal object PermissionManager { } } val activity = (context as? Activity) - ?: throw KauException("Context is not an instance of an activity; cannot request permissions") + ?: throw KauException("Context is not an instance of an activity; cannot request permissions") KL.i { "Requesting permissions ${permissions.contentToString()}" } ActivityCompat.requestPermissions(activity, permissions, 1) } /** - * Handles permission result by allowing accepted permissions for all pending requests - * Also cleans up destroyed or completed pending requests + * Handles permission result by allowing accepted permissions for all pending requests. + * Also cleans up destroyed or completed pending requests. */ fun onRequestPermissionsResult(context: Context, permissions: Array<out String>, grantResults: IntArray) { KL.i { "On permission result: pending ${pendingResults.size}" } val count = Math.min(permissions.size, grantResults.size) - val iter = pendingResults.iterator() - while (iter.hasNext()) { - val action = iter.next().get() - if ((0 until count).any { action?.onResult(permissions[it], grantResults[it]) != false }) - iter.remove() + pendingResults.kauRemoveIf { + val action = it.get() + action == null || (0 until count).any { i -> action.onResult(permissions[i], grantResults[i]) } } - if (pendingResults.isEmpty()) - requestInProgress = false - else { - val action = pendingResults.map { it.get() }.firstOrNull { it != null } - if (action == null) { //actions have been unlinked from their weak references - pendingResults.clear() - requestInProgress = false - return - } - requestPermissions(context, action.permissions.toTypedArray()) + val action = pendingResults.asSequence().map { it.get() }.firstOrNull { it != null } + if (action == null) { // actions have been unlinked from their weak references + pendingResults.clear() + return } - KL.i { "Post on permission result: pending ${pendingResults.size}" } + requestPermissions(context, action.permissions.toTypedArray()) } - -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt index ba3e6dd..599f6e8 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/PermissionResult.kt @@ -1,3 +1,18 @@ +/* + * 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.permissions import android.content.pm.PackageManager @@ -21,8 +36,10 @@ class PermissionResult(permissions: Array<out String>, val callback: (granted: B return true } permissions.remove(permission) - if (permissions.isNotEmpty()) return false + if (permissions.isNotEmpty()) { + return false + } callback(true, null) return true } -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt b/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt index 248e484..3c90b05 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/permissions/Permissions.kt @@ -1,11 +1,25 @@ +/* + * 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.permissions import android.Manifest import android.app.Activity import android.content.Context import android.os.Build -import android.support.annotation.RequiresApi - +import androidx.annotation.RequiresApi /** * Created by Allan Wang on 2017-07-02. @@ -22,7 +36,8 @@ import android.support.annotation.RequiresApi /** * Hook that should be added inside all [Activity.onRequestPermissionsResult] so that the Permission manager can handle the responses */ -fun Activity.kauOnRequestPermissionsResult(permissions: Array<out String>, grantResults: IntArray) = PermissionManager.onRequestPermissionsResult(this, permissions, grantResults) +fun Activity.kauOnRequestPermissionsResult(permissions: Array<out String>, grantResults: IntArray) = + PermissionManager.onRequestPermissionsResult(this, permissions, grantResults) /** * Request a permission with a callback @@ -31,7 +46,10 @@ fun Activity.kauOnRequestPermissionsResult(permissions: Array<out String>, grant * The [callback] returns [granted], which is true if all permissions are granted * [deniedPerm] is the first denied permission, if granted is false */ -fun Context.kauRequestPermissions(vararg permissions: String, callback: (granted: Boolean, deniedPerm: String?) -> Unit) = PermissionManager(this, permissions, callback) +fun Context.kauRequestPermissions( + vararg permissions: String, + callback: (granted: Boolean, deniedPerm: String?) -> Unit +) = PermissionManager(this, permissions, callback) /** * See http://developer.android.com/guide/topics/security/permissions.html#normal-dangerous for a @@ -72,4 +90,4 @@ const val PERMISSION_RECEIVE_MMS = Manifest.permission.RECEIVE_MMS const val PERMISSION_READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE const val PERMISSION_WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE -const val PERMISSION_SYSTEM_ALERT_WINDOW = Manifest.permission.SYSTEM_ALERT_WINDOW
\ No newline at end of file +const val PERMISSION_SYSTEM_ALERT_WINDOW = Manifest.permission.SYSTEM_ALERT_WINDOW diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/RelativeSlider.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/RelativeSlider.kt index f14f5cf..0b1dd88 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/RelativeSlider.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/RelativeSlider.kt @@ -1,3 +1,18 @@ +/* + * 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.swipe /** @@ -30,10 +45,14 @@ internal class RelativeSlider(var curPage: SwipeBackPage) : SwipeListener { return } when (edgeFlag) { - SWIPE_EDGE_LEFT -> page.swipeBackLayout.x = Math.min(-offset * Math.max(1 - percent, 0f) + DEFAULT_OFFSET, 0f) - SWIPE_EDGE_RIGHT -> page.swipeBackLayout.x = Math.min(offset * Math.max(1 - percent, 0f) - DEFAULT_OFFSET, 0f) - SWIPE_EDGE_TOP -> page.swipeBackLayout.y = Math.min(-offset * Math.max(1 - percent, 0f) + DEFAULT_OFFSET, 0f) - SWIPE_EDGE_BOTTOM -> page.swipeBackLayout.y = Math.min(offset * Math.max(1 - percent, 0f) - DEFAULT_OFFSET, 0f) + SWIPE_EDGE_LEFT -> page.swipeBackLayout.x = + Math.min(-offset * Math.max(1 - percent, 0f) + DEFAULT_OFFSET, 0f) + SWIPE_EDGE_RIGHT -> page.swipeBackLayout.x = + Math.min(offset * Math.max(1 - percent, 0f) - DEFAULT_OFFSET, 0f) + SWIPE_EDGE_TOP -> page.swipeBackLayout.y = + Math.min(-offset * Math.max(1 - percent, 0f) + DEFAULT_OFFSET, 0f) + SWIPE_EDGE_BOTTOM -> page.swipeBackLayout.y = + Math.min(offset * Math.max(1 - percent, 0f) - DEFAULT_OFFSET, 0f) } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt index 018d7c7..1c6de12 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackHelper.kt @@ -1,3 +1,18 @@ +/* + * 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.swipe import android.app.Activity @@ -5,7 +20,7 @@ import ca.allanwang.kau.R import ca.allanwang.kau.kotlin.kauRemoveIf import ca.allanwang.kau.logging.KL import ca.allanwang.kau.swipe.SwipeBackHelper.onDestroy -import java.util.* +import java.util.Stack /** * Singleton to hold our swipe stack @@ -16,7 +31,8 @@ internal object SwipeBackHelper { private val pageStack = Stack<SwipeBackPage>() - private operator fun get(activity: Activity): SwipeBackPage? = pageStack.firstOrNull { it.activityRef.get() === activity } + private operator fun get(activity: Activity): SwipeBackPage? = + pageStack.firstOrNull { it.activityRef.get() === activity } fun onCreate(activity: Activity, builder: SwipeBackContract.() -> Unit = {}) { val page = this[activity] ?: pageStack.push(SwipeBackPage(activity).apply { builder() }) @@ -93,4 +109,4 @@ const val SWIPE_EDGE_RIGHT = ViewDragHelper.EDGE_RIGHT const val SWIPE_EDGE_TOP = ViewDragHelper.EDGE_TOP -const val SWIPE_EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM
\ No newline at end of file +const val SWIPE_EDGE_BOTTOM = ViewDragHelper.EDGE_BOTTOM diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt index a323e6c..1f564ce 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackLayout.kt @@ -1,15 +1,30 @@ +/* + * 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.swipe import android.annotation.SuppressLint import android.app.Activity import android.content.Context import android.graphics.Canvas -import android.support.v4.view.ViewCompat import android.util.AttributeSet import android.view.MotionEvent import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import androidx.core.view.ViewCompat import ca.allanwang.kau.logging.KL import ca.allanwang.kau.utils.adjustAlpha import ca.allanwang.kau.utils.navigationBarColor @@ -23,7 +38,10 @@ import java.lang.ref.WeakReference * If an edge detection occurs, this layout consumes all the touch events * Use the [swipeEnabled] toggle if you need the scroll events on the same axis */ -internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0 +internal class SwipeBackLayout @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0 ) : FrameLayout(context, attrs, defStyle), SwipeBackContract, SwipeBackContractInternal { override val swipeBackLayout: SwipeBackLayout @@ -96,11 +114,9 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs override fun onEdgeTouch() {} override fun onScrollToClose(edgeFlag: Int) {} - } } - private var inLayout: Boolean = false override var edgeSize: Int @@ -158,7 +174,8 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs } override fun setEdgeSizePercent(swipeEdgePercent: Float) { - edgeSize = ((if (horizontal) resources.displayMetrics.widthPixels else resources.displayMetrics.heightPixels) * swipeEdgePercent).toInt() + edgeSize = + ((if (horizontal) resources.displayMetrics.widthPixels else resources.displayMetrics.heightPixels) * swipeEdgePercent).toInt() } /** @@ -206,7 +223,7 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs */ override fun scrollToFinishActivity() { val contentView = contentViewRef.get() - ?: return KL.e { "KauSwipe cannot scroll to finish as contentView is null. Is onPostCreate called?" } + ?: return KL.e { "KauSwipe cannot scroll to finish as contentView is null. Is onPostCreate called?" } val swipeWidth = contentView.width + OVERSCROLL_DISTANCE val swipeHeight = contentView.height + OVERSCROLL_DISTANCE var top = 0 @@ -243,7 +260,7 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { val contentView = contentViewRef.get() - ?: return KL.e { "KauSwipe cannot change layout as contentView is null. Is onPostCreate called?" } + ?: return KL.e { "KauSwipe cannot change layout as contentView is null. Is onPostCreate called?" } inLayout = true val xOffset: Int val yOffset: Int @@ -346,11 +363,12 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs override fun onViewPositionChanged(changedView: View, left: Int, top: Int, dx: Int, dy: Int) { super.onViewPositionChanged(changedView, left, top, dx, dy) val contentView = contentViewRef.get() - ?: return KL.e { "KauSwipe cannot change view position as contentView is null; is onPostCreate called?" } + ?: return KL.e { "KauSwipe cannot change view position as contentView is null; is onPostCreate called?" } //make sure that we are using the proper axis scrollPercent = Math.abs( - if (horizontal) left.toFloat() / contentView.width - else (top.toFloat() / contentView.height)) + if (horizontal) left.toFloat() / contentView.width + else (top.toFloat() / contentView.height) + ) contentOffset = if (horizontal) left else top invalidate() if (scrollPercent < scrollThreshold && !isScrollOverValid) @@ -375,10 +393,11 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs var result = Pair(0, 0) if (scrollPercent <= scrollThreshold) { //threshold not met; check velocities - if ((edgeFlag == SWIPE_EDGE_LEFT && xvel > MIN_FLING_VELOCITY) - || (edgeFlag == SWIPE_EDGE_RIGHT && xvel < -MIN_FLING_VELOCITY) - || (edgeFlag == SWIPE_EDGE_TOP && yvel > MIN_FLING_VELOCITY) - || (edgeFlag == SWIPE_EDGE_BOTTOM && yvel < -MIN_FLING_VELOCITY)) + if ((edgeFlag == SWIPE_EDGE_LEFT && xvel > MIN_FLING_VELOCITY) || + (edgeFlag == SWIPE_EDGE_RIGHT && xvel < -MIN_FLING_VELOCITY) || + (edgeFlag == SWIPE_EDGE_TOP && yvel > MIN_FLING_VELOCITY) || + (edgeFlag == SWIPE_EDGE_BOTTOM && yvel < -MIN_FLING_VELOCITY) + ) result = exitCaptureOffsets(edgeFlag, releasedChild) } else { //threshold met; fling to designated side @@ -431,6 +450,5 @@ internal class SwipeBackLayout @JvmOverloads constructor(context: Context, attrs const val DEFAULT_SCROLL_THRESHOLD = 0.3f const val OVERSCROLL_DISTANCE = 10 - } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt index 4dba622..bc5b459 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeBackPage.kt @@ -1,3 +1,18 @@ +/* + * 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.swipe import android.app.Activity @@ -24,7 +39,8 @@ internal class SwipeBackPage(activity: Activity) : SwipeBackContractInternal by init { activity.window.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) activity.window.decorView.setBackgroundColor(Color.TRANSPARENT) - swipeBackLayout.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + swipeBackLayout.layoutParams = + ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) slider = RelativeSlider(this) } @@ -41,7 +57,7 @@ internal class SwipeBackPage(activity: Activity) : SwipeBackContractInternal by private fun handleLayout() { val activity = activityRef.get() - ?: return KL.v { "KauSwipe activity ref gone during handleLayout" } + ?: return KL.v { "KauSwipe activity ref gone during handleLayout" } if (swipeEnabled) swipeBackLayout.attachToActivity(activity) else swipeBackLayout.removeFromActivity(activity) } @@ -50,7 +66,6 @@ internal class SwipeBackPage(activity: Activity) : SwipeBackContractInternal by swipeBackLayout.scrollThreshold = percent return this } - } internal interface SwipeBackContractInternal : SwipeBackContract { @@ -104,4 +119,4 @@ interface SwipeBackContract { fun removeListener(listener: SwipeListener) fun hasListener(listener: SwipeListener): Boolean fun scrollToFinishActivity() -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeListener.kt b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeListener.kt index 3ea62b5..07f8eb8 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeListener.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/SwipeListener.kt @@ -1,3 +1,18 @@ +/* + * 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.swipe interface SwipeListener { @@ -16,4 +31,4 @@ interface SwipeListener { * Invoked when scroll percent over the threshold for the first time */ fun onScrollToClose(edgeFlag: Int) -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java b/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java index 566e9e5..ac7fb7e 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java +++ b/core/src/main/kotlin/ca/allanwang/kau/swipe/ViewDragHelper.java @@ -1,7 +1,7 @@ package ca.allanwang.kau.swipe; import android.content.Context; -import android.support.v4.view.ViewCompat; +import androidx.core.view.ViewCompat; import android.util.Log; import android.view.MotionEvent; import android.view.VelocityTracker; @@ -25,7 +25,7 @@ import static ca.allanwang.kau.swipe.SwipeBackHelperKt.SWIPE_EDGE_TOP; * of useful operations and state tracking for allowing a user to drag and reposition * views within their parent ViewGroup. * <p> - * This is an extension of {@link android.support.v4.widget.ViewDragHelper} + * This is an extension of {@link androidx.core.widget.ViewDragHelper} * Along with additional methods defined in {@link ViewDragHelperExtras} */ class ViewDragHelper implements ViewDragHelperExtras { diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt index b98a47e..a46e6c5 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/ProgressAnimator.kt @@ -1,9 +1,24 @@ +/* + * 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.ui import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator -import android.support.annotation.VisibleForTesting +import androidx.annotation.VisibleForTesting import ca.allanwang.kau.kotlin.kauRemoveIf /** @@ -37,8 +52,7 @@ class ProgressAnimator private constructor() : ValueAnimator() { isCancelled = false } }) - apply(builder) - } + }.apply(builder) /** * Gets output of a linear function starting at [start] when [progress] is 0 and [end] when [progress] is 1 at point [progress]. @@ -54,7 +68,6 @@ class ProgressAnimator private constructor() : ValueAnimator() { start + (end - start) * trueProgress } } - } private val animators: MutableList<ProgressDisposableAction> = mutableListOf() @@ -69,7 +82,6 @@ class ProgressAnimator private constructor() : ValueAnimator() { val animatorCount get() = animators.size - /** * Converts an action to a disposable action */ @@ -95,13 +107,13 @@ class ProgressAnimator private constructor() : ValueAnimator() { } fun withAnimator(action: ProgressAction) = - withDisposableAnimator(action.asDisposable()) + withDisposableAnimator(action.asDisposable()) /** * Range animator. Multiples the range by the current float progress before emission */ fun withAnimator(from: Float, to: Float, action: ProgressAction) = - withDisposableAnimator(from, to, action.asDisposable()) + withDisposableAnimator(from, to, action.asDisposable()) fun withDisposableAnimator(action: ProgressDisposableAction) = animators.add(action) @@ -170,4 +182,4 @@ class ProgressAnimator private constructor() : ValueAnimator() { private typealias ProgressAction = (Float) -> Unit private typealias ProgressDisposableAction = (Float) -> Boolean private typealias ProgressRunnable = () -> Unit -private typealias ProgressDisposableRunnable = () -> Boolean
\ No newline at end of file +private typealias ProgressDisposableRunnable = () -> Boolean diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt index b92b222..684a11c 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/SimpleRippleDrawable.kt @@ -1,11 +1,26 @@ +/* + * 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.ui import android.content.res.ColorStateList import android.graphics.drawable.ColorDrawable import android.graphics.drawable.RippleDrawable import android.os.Build -import android.support.annotation.ColorInt -import android.support.annotation.RequiresApi +import androidx.annotation.ColorInt +import androidx.annotation.RequiresApi import ca.allanwang.kau.utils.adjustAlpha /** @@ -19,4 +34,4 @@ fun createSimpleRippleDrawable(@ColorInt foregroundColor: Int, @ColorInt backgro val content = ColorDrawable(backgroundColor) val mask = ColorDrawable(foregroundColor.adjustAlpha(0.16f)) return RippleDrawable(states, content, mask) -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt index 8923775..b2a0d27 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/CollapsibleViewDelegate.kt @@ -1,8 +1,27 @@ +/* + * 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.ui.views import android.animation.ValueAnimator import android.view.View -import ca.allanwang.kau.utils.* +import ca.allanwang.kau.utils.KAU_COLLAPSED +import ca.allanwang.kau.utils.KAU_COLLAPSING +import ca.allanwang.kau.utils.KAU_EXPANDED +import ca.allanwang.kau.utils.KAU_EXPANDING +import ca.allanwang.kau.utils.goneIf import java.lang.ref.WeakReference /** @@ -48,10 +67,10 @@ class CollapsibleViewDelegate : CollapsibleView { if (v > 1) v = 1f else if (v < 0) v = 0f stateHolder = - if (v == 0f) KAU_COLLAPSED - else if (v == 1f) KAU_EXPANDED - else if (v - field < 0) KAU_COLLAPSING - else KAU_EXPANDING + if (v == 0f) KAU_COLLAPSED + else if (v == 1f) KAU_EXPANDED + else if (v - field < 0) KAU_COLLAPSING + else KAU_EXPANDING field = v view?.goneIf(state == KAU_COLLAPSED) view?.requestLayout() @@ -101,5 +120,4 @@ class CollapsibleViewDelegate : CollapsibleView { val target = if (expand) 1f else 0f if (animate) animateSize(target) else expansion = target } - -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt index 716fd71..6481306 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/MeasureSpecDelegate.kt @@ -1,3 +1,18 @@ +/* + * 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.ui.views import android.content.Context @@ -84,10 +99,13 @@ class MeasureSpecDelegate : MeasureSpecContract { val styledAttrs = context.obtainStyledAttributes(attrs, R.styleable.MeasureSpecDelegate) relativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidth, relativeWidth) relativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeight, relativeHeight) - relativeWidthToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidthToParent, relativeWidthToParent) - relativeHeightToParent = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeightToParent, relativeHeightToParent) + relativeWidthToParent = + styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeWidthToParent, relativeWidthToParent) + relativeHeightToParent = + styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_relativeHeightToParent, relativeHeightToParent) postRelativeWidth = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeWidth, postRelativeWidth) - postRelativeHeight = styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeHeight, postRelativeHeight) + postRelativeHeight = + styledAttrs.getFloat(R.styleable.MeasureSpecDelegate_postRelativeHeight, postRelativeHeight) styledAttrs.recycle() } @@ -115,5 +133,4 @@ class MeasureSpecDelegate : MeasureSpecContract { private val Float.measureSpec: Int get() = View.MeasureSpec.makeMeasureSpec(this.toInt(), View.MeasureSpec.EXACTLY) - } diff --git a/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt b/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt index 3d86419..176b9ea 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/ui/views/RippleCanvas.kt @@ -1,3 +1,18 @@ +/* + * 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.ui.views import android.animation.Animator @@ -5,7 +20,11 @@ import android.animation.AnimatorListenerAdapter import android.animation.ArgbEvaluator import android.animation.ValueAnimator import android.content.Context -import android.graphics.* +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.PorterDuff +import android.graphics.PorterDuffXfermode import android.util.AttributeSet import android.view.View @@ -17,7 +36,9 @@ import android.view.View * Supports multiple ripples from varying locations */ class RippleCanvas @JvmOverloads constructor( - context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 ) : View(context, attrs, defStyleAttr) { private val paint: Paint = Paint().apply { isAntiAlias = true @@ -53,11 +74,13 @@ class RippleCanvas @JvmOverloads constructor( /** * Creates a ripple effect from the given starting values */ - fun ripple(color: Int, - startX: Float = 0f, - startY: Float = 0f, - duration: Long = 600L, - callback: (() -> Unit)? = null) { + fun ripple( + color: Int, + startX: Float = 0f, + startY: Float = 0f, + duration: Long = 600L, + callback: (() -> Unit)? = null + ) { val w = width.toFloat() val h = height.toFloat() val x = when (startX) { @@ -112,11 +135,13 @@ class RippleCanvas @JvmOverloads constructor( animator.start() } - internal class Ripple(val color: Int, - val x: Float, - val y: Float, - var radius: Float, - val maxRadius: Float) + internal class Ripple( + val color: Int, + val x: Float, + val y: Float, + var radius: Float, + val maxRadius: Float + ) companion object { const val MIDDLE = -1.0f 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 ab0e59f..21141f1 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ActivityUtils.kt @@ -1,3 +1,18 @@ +/* + * 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. + */ @file:Suppress("NOTHING_TO_INLINE") package ca.allanwang.kau.utils @@ -11,15 +26,15 @@ import android.content.Intent import android.graphics.Color import android.os.Build import android.os.Bundle -import android.support.annotation.ColorInt -import android.support.annotation.RequiresApi -import android.support.annotation.StringRes -import android.support.design.widget.Snackbar 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. @@ -36,19 +51,22 @@ annotation class KauActivity */ @Suppress("DEPRECATION") inline fun <reified T : Activity> Activity.startActivityForResult( - requestCode: Int, - bundleBuilder: Bundle.() -> Unit = {}, - intentBuilder: Intent.() -> Unit = {} + requestCode: Int, + bundleBuilder: Bundle.() -> Unit = {}, + intentBuilder: Intent.() -> Unit = {} ) = startActivityForResult(T::class.java, requestCode, bundleBuilder, intentBuilder) -@Deprecated("Use reified generic instead of passing class", - ReplaceWith("startActivityForResult<T>(requestCode, bundleBuilder, intentBuilder)"), - DeprecationLevel.WARNING) +@Deprecated( + "Use reified generic instead of passing class", + ReplaceWith("startActivityForResult<T>(requestCode, bundleBuilder, intentBuilder)"), + DeprecationLevel.WARNING +) inline fun <T : Activity> Activity.startActivityForResult( - clazz: Class<T>, - requestCode: Int, - bundleBuilder: Bundle.() -> Unit = {}, - intentBuilder: Intent.() -> Unit = {}) { + clazz: Class<T>, + requestCode: Int, + bundleBuilder: Bundle.() -> Unit = {}, + intentBuilder: Intent.() -> Unit = {} +) { val intent = Intent(this, clazz) intent.intentBuilder() val bundle = Bundle() @@ -72,13 +90,12 @@ inline fun Activity.restart(intentBuilder: Intent.() -> Unit = {}) { overridePendingTransition(R.anim.kau_fade_in, R.anim.kau_fade_out) } - /** * Force restart an entire application */ @RequiresApi(Build.VERSION_CODES.M) inline fun Activity.restartApplication() { - val intent = packageManager.getLaunchIntentForPackage(packageName) + val intent = packageManager.getLaunchIntentForPackage(packageName)!! intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) val pending = PendingIntent.getActivity(this, 666, intent, PendingIntent.FLAG_CANCEL_CURRENT) val alarm = getSystemService(Context.ALARM_SERVICE) as AlarmManager @@ -119,8 +136,8 @@ inline var Activity.statusBarLight: Boolean if (buildIsMarshmallowAndUp) { val flags = window.decorView.systemUiVisibility window.decorView.systemUiVisibility = - if (value) flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR - else flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() + if (value) flags or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR + else flags and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv() } } @@ -135,10 +152,33 @@ fun Context.setMenuIcons(menu: Menu, @ColorInt color: Int = Color.WHITE, vararg } } -inline fun Activity.hideKeyboard() = currentFocus.hideKeyboard() - -inline fun Activity.showKeyboard() = currentFocus.showKeyboard() +inline fun Activity.hideKeyboard() { + currentFocus?.hideKeyboard() +} -inline fun Activity.snackbar(text: String, duration: Int = Snackbar.LENGTH_LONG, noinline builder: Snackbar.() -> Unit = {}) = contentView!!.snackbar(text, duration, builder) +inline fun Activity.showKeyboard() { + currentFocus?.showKeyboard() +} -inline fun Activity.snackbar(@StringRes textId: Int, duration: Int = Snackbar.LENGTH_LONG, noinline builder: Snackbar.() -> Unit = {}) = contentView!!.snackbar(textId, duration, builder)
\ No newline at end of file +/** + * 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, + noinline builder: Snackbar.() -> Unit = {} +) = contentView!!.snackbar(text, duration, builder) + +inline fun Activity.snackbar( + @StringRes textId: Int, + duration: Int = Snackbar.LENGTH_LONG, + noinline builder: Snackbar.() -> Unit = {} +) = contentView!!.snackbar(textId, duration, builder) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt index 2767f04..0062361 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/AnimHolder.kt @@ -1,7 +1,22 @@ +/* + * 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.utils import android.os.Build -import android.support.annotation.RequiresApi +import androidx.annotation.RequiresApi import ca.allanwang.kau.kotlin.lazyInterpolator /** @@ -14,5 +29,4 @@ object AnimHolder { @RequiresApi(Build.VERSION_CODES.LOLLIPOP) val fastOutSlowInInterpolator = lazyInterpolator(android.R.interpolator.fast_out_linear_in) val decelerateInterpolator = lazyInterpolator(android.R.interpolator.decelerate_cubic) - -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt index a287cf2..0a548ce 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/AnimUtils.kt @@ -1,15 +1,30 @@ +/* + * 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.utils import android.animation.Animator import android.animation.AnimatorListenerAdapter import android.annotation.SuppressLint -import android.support.annotation.StringRes import android.view.View import android.view.ViewAnimationUtils import android.view.ViewPropertyAnimator import android.view.animation.Animation import android.view.animation.AnimationUtils import android.widget.TextView +import androidx.annotation.StringRes /** * Created by Allan Wang on 2017-06-01. @@ -19,7 +34,15 @@ import android.widget.TextView @SuppressLint("NewApi") @KauUtils -fun View.circularReveal(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = -1.0f, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) { +fun View.circularReveal( + x: Int = 0, + y: Int = 0, + offset: Long = 0L, + radius: Float = -1.0f, + duration: Long = 500L, + onStart: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null +) { if (!isAttachedToWindow) { onStart?.invoke() visible() @@ -29,7 +52,10 @@ fun View.circularReveal(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float if (!buildIsLollipopAndUp) return fadeIn(offset, duration, onStart, onFinish) val r = if (radius >= 0) radius - else Math.max(Math.hypot(x.toDouble(), y.toDouble()), Math.hypot((width - x.toDouble()), (height - y.toDouble()))).toFloat() + else Math.max( + Math.hypot(x.toDouble(), y.toDouble()), + Math.hypot((width - x.toDouble()), (height - y.toDouble())) + ).toFloat() val anim = ViewAnimationUtils.createCircularReveal(this, x, y, 0f, r).setDuration(duration) anim.startDelay = offset @@ -47,7 +73,15 @@ fun View.circularReveal(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float @SuppressLint("NewApi") @KauUtils -fun View.circularHide(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = -1.0f, duration: Long = 500L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) { +fun View.circularHide( + x: Int = 0, + y: Int = 0, + offset: Long = 0L, + radius: Float = -1.0f, + duration: Long = 500L, + onStart: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null +) { if (!isAttachedToWindow) { onStart?.invoke() invisible() @@ -57,7 +91,10 @@ fun View.circularHide(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = if (!buildIsLollipopAndUp) return fadeOut(offset, duration, onStart, onFinish) val r = if (radius >= 0) radius - else Math.max(Math.hypot(x.toDouble(), y.toDouble()), Math.hypot((width - x.toDouble()), (height - y.toDouble()))).toFloat() + else Math.max( + Math.hypot(x.toDouble(), y.toDouble()), + Math.hypot((width - x.toDouble()), (height - y.toDouble())) + ).toFloat() val anim = ViewAnimationUtils.createCircularReveal(this, x, y, r, 0f).setDuration(duration) anim.startDelay = offset @@ -75,7 +112,12 @@ fun View.circularHide(x: Int = 0, y: Int = 0, offset: Long = 0L, radius: Float = } @KauUtils -fun View.fadeIn(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) { +fun View.fadeIn( + offset: Long = 0L, + duration: Long = 200L, + onStart: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null +) { if (!isAttachedToWindow) { onStart?.invoke() visible() @@ -97,7 +139,12 @@ fun View.fadeIn(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? } @KauUtils -fun View.fadeOut(offset: Long = 0L, duration: Long = 200L, onStart: (() -> Unit)? = null, onFinish: (() -> Unit)? = null) { +fun View.fadeOut( + offset: Long = 0L, + duration: Long = 200L, + onStart: (() -> Unit)? = null, + onFinish: (() -> Unit)? = null +) { if (!isAttachedToWindow) { onStart?.invoke() invisible() @@ -130,7 +177,8 @@ fun TextView.setTextWithFade(text: String, duration: Long = 200, onFinish: (() - } @KauUtils -fun TextView.setTextWithFade(@StringRes textId: Int, duration: Long = 200, onFinish: (() -> Unit)? = null) = setTextWithFade(context.getString(textId), duration, onFinish) +fun TextView.setTextWithFade(@StringRes textId: Int, duration: Long = 200, onFinish: (() -> Unit)? = null) = + setTextWithFade(context.getString(textId), duration, onFinish) @KauUtils -fun ViewPropertyAnimator.scaleXY(value: Float) = scaleX(value).scaleY(value)
\ No newline at end of file +fun ViewPropertyAnimator.scaleXY(value: Float) = scaleX(value).scaleY(value) 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 82cd577..40c1c72 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/BundleUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.annotation.SuppressLint @@ -5,10 +20,12 @@ import android.app.Activity import android.app.ActivityOptions import android.content.Context import android.os.Bundle -import android.support.annotation.AnimRes +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. @@ -22,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") @@ -36,34 +103,46 @@ fun Bundle.withSceneTransitionAnimation(context: Context) { * create a scene transition animation */ fun Bundle.withSceneTransitionAnimation(parent: View, data: Map<Int, String>) = - withSceneTransitionAnimation(parent.context, data.mapKeys { (id, _) -> - parent.findViewById<View>(id) - }) + withSceneTransitionAnimation(parent.context, data.mapKeys { (id, _) -> + parent.findViewById<View>(id) + }) /** * Given a mapping of views to tags, * create a scene transition animation */ @SuppressLint("NewApi") -fun Bundle.withSceneTransitionAnimation(context: Context, data: Map<View, String>) { +fun Bundle.withSceneTransitionAnimation(context: Context, data: Map<out View, String>) { if (context !is Activity || !buildIsLollipopAndUp) return - val options = ActivityOptions.makeSceneTransitionAnimation(context, - *data.map { (view, tag) -> Pair(view, tag) }.toTypedArray()) + val options = ActivityOptions.makeSceneTransitionAnimation( + context, + *data.map { (view, tag) -> Pair(view, tag) }.toTypedArray() + ) putAll(options.toBundle()) } -fun Bundle.withCustomAnimation(context: Context, - @AnimRes enterResId: Int, - @AnimRes exitResId: Int) { - this with ActivityOptions.makeCustomAnimation(context, - enterResId, exitResId).toBundle() +fun Bundle.withCustomAnimation( + context: Context, + @AnimRes enterResId: Int, + @AnimRes exitResId: Int +) { + this with ActivityOptions.makeCustomAnimation( + context, + enterResId, exitResId + ).toBundle() } -fun Bundle.withSlideIn(context: Context) = withCustomAnimation(context, - R.anim.kau_slide_in_right, R.anim.kau_fade_out) +fun Bundle.withSlideIn(context: Context) = withCustomAnimation( + context, + R.anim.kau_slide_in_right, R.anim.kau_fade_out +) -fun Bundle.withSlideOut(context: Context) = withCustomAnimation(context, - R.anim.kau_fade_in, R.anim.kau_slide_out_right_top) +fun Bundle.withSlideOut(context: Context) = withCustomAnimation( + context, + R.anim.kau_fade_in, R.anim.kau_slide_out_right_top +) -fun Bundle.withFade(context: Context) = withCustomAnimation(context, - android.R.anim.fade_in, android.R.anim.fade_out)
\ No newline at end of file +fun Bundle.withFade(context: Context) = withCustomAnimation( + context, + android.R.anim.fade_in, android.R.anim.fade_out +) 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 236f2ca..bbb8953 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ColorUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.annotation.SuppressLint @@ -7,15 +22,21 @@ import android.graphics.Color import android.graphics.PorterDuff import android.graphics.drawable.Drawable import android.os.Build -import android.support.annotation.ColorInt -import android.support.annotation.FloatRange -import android.support.annotation.IntRange -import android.support.v4.graphics.drawable.DrawableCompat -import android.support.v7.widget.AppCompatEditText -import android.support.v7.widget.Toolbar -import android.widget.* +import android.widget.CheckBox +import android.widget.EditText +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 com.afollestad.materialdialogs.R -import java.util.* +import java.util.Random /** * Created by Allan Wang on 2017-06-08. @@ -38,7 +59,7 @@ inline val Int.isColorDark: Boolean get() = isColorDark(0.5f) fun Int.isColorDark(minDarkness: Float): Boolean = - ((0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255.0) < minDarkness + ((0.299 * Color.red(this) + 0.587 * Color.green(this) + 0.114 * Color.blue(this)) / 255.0) < minDarkness fun Int.toHexString(withAlpha: Boolean = false, withHexPrefix: Boolean = true): String { val hex = if (withAlpha) String.format("#%08X", this) @@ -46,7 +67,8 @@ fun Int.toHexString(withAlpha: Boolean = false, withHexPrefix: Boolean = true): return if (withHexPrefix) hex else hex.substring(1) } -fun Int.toRgbaString(): String = "rgba(${Color.red(this)}, ${Color.green(this)}, ${Color.blue(this)}, ${(Color.alpha(this) / 255f).round(3)})" +fun Int.toRgbaString(): String = + "rgba(${Color.red(this)}, ${Color.green(this)}, ${Color.blue(this)}, ${(Color.alpha(this) / 255f).round(3)})" fun Int.toHSV(): FloatArray { val hsv = FloatArray(3) @@ -59,13 +81,15 @@ inline val Int.isColorOpaque: Boolean fun FloatArray.toColor(): Int = Color.HSVToColor(this) -fun Int.isColorVisibleOn(@ColorInt color: Int, @IntRange(from = 0L, to = 255L) delta: Int = 25, - @IntRange(from = 0L, to = 255L) minAlpha: Int = 50): Boolean = - if (Color.alpha(this) < minAlpha) false - else !(Math.abs(Color.red(this) - Color.red(color)) < delta - && Math.abs(Color.green(this) - Color.green(color)) < delta - && Math.abs(Color.blue(this) - Color.blue(color)) < delta) - +fun Int.isColorVisibleOn( + @ColorInt color: Int, + @IntRange(from = 0L, to = 255L) delta: Int = 25, + @IntRange(from = 0L, to = 255L) minAlpha: Int = 50 +): Boolean = + if (Color.alpha(this) < minAlpha) false + else !(Math.abs(Color.red(this) - Color.red(color)) < delta && + Math.abs(Color.green(this) - Color.green(color)) < delta && + Math.abs(Color.blue(this) - Color.blue(color)) < delta) @ColorInt fun Context.getDisabledColor(): Int { @@ -94,49 +118,58 @@ fun Int.blendWith(@ColorInt color: Int, @FloatRange(from = 0.0, to = 1.0) ratio: } @ColorInt -fun Int.withAlpha(@IntRange(from = 0L, to = 255L) alpha: Int): Int = Color.argb(alpha, Color.red(this), Color.green(this), Color.blue(this)) +fun Int.withAlpha(@IntRange(from = 0, to = 0xFF) alpha: Int): Int = + this and 0x00FFFFFF or (alpha shl 24) @ColorInt -fun Int.withMinAlpha(@IntRange(from = 0L, to = 255L) alpha: Int): Int = Color.argb(Math.max(alpha, Color.alpha(this)), Color.red(this), Color.green(this), Color.blue(this)) +fun Int.withMinAlpha(@IntRange(from = 0, to = 0xFF) alpha: Int): Int = + withAlpha(Math.max(alpha, this ushr 24)) @ColorInt -fun Int.lighten(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int { +private inline fun Int.colorFactor(rgbFactor: (Int) -> Float): Int { val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this)) - .map { (it * (1f - factor) + 255f * factor).toInt() } + .map { rgbFactor(it).toInt() } return Color.argb(Color.alpha(this), red, green, blue) } @ColorInt -fun Int.darken(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int { - val (red, green, blue) = intArrayOf(Color.red(this), Color.green(this), Color.blue(this)) - .map { (it * (1f - factor)).toInt() } - return Color.argb(Color.alpha(this), red, green, blue) +fun Int.lighten(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = colorFactor { + (it * (1f - factor) + 255f * factor) } @ColorInt -fun Int.colorToBackground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = if (isColorDark) darken(factor) else lighten(factor) +fun Int.darken(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = colorFactor { + it * (1f - factor) +} + +@ColorInt +fun Int.colorToBackground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = + if (isColorDark) darken(factor) else lighten(factor) @ColorInt -fun Int.colorToForeground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = if (isColorDark) lighten(factor) else darken(factor) +fun Int.colorToForeground(@FloatRange(from = 0.0, to = 1.0) factor: Float = 0.1f): Int = + if (isColorDark) lighten(factor) else darken(factor) -@Throws(IllegalArgumentException::class) fun String.toColor(): Int { - val toParse: String - if (startsWith("#") && length == 4) - toParse = "#${this[1]}${this[1]}${this[2]}${this[2]}${this[3]}${this[3]}" + val toParse: String = if (startsWith("#") && length == 4) + "#${this[1]}${this[1]}${this[2]}${this[2]}${this[3]}${this[3]}" else - toParse = this + this return Color.parseColor(toParse) } //Get ColorStateList fun Context.colorStateList(@ColorInt color: Int): ColorStateList { val disabledColor = color.adjustAlpha(0.3f) - return ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), + return ColorStateList( + arrayOf( + intArrayOf(android.R.attr.state_enabled, -android.R.attr.state_checked), intArrayOf(android.R.attr.state_enabled, android.R.attr.state_checked), intArrayOf(-android.R.attr.state_enabled, -android.R.attr.state_checked), - intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked)), - intArrayOf(color.adjustAlpha(0.8f), color, disabledColor, disabledColor)) + intArrayOf(-android.R.attr.state_enabled, android.R.attr.state_checked) + ), + intArrayOf(color.adjustAlpha(0.8f), color, disabledColor, disabledColor) + ) } /* @@ -203,14 +236,14 @@ fun ProgressBar.tint(@ColorInt color: Int, skipIndeterminate: Boolean = false) { fun Context.textColorStateList(@ColorInt color: Int): ColorStateList { val states = arrayOf( - intArrayOf(-android.R.attr.state_enabled), - intArrayOf(-android.R.attr.state_pressed, -android.R.attr.state_focused), - intArrayOf() + intArrayOf(-android.R.attr.state_enabled), + intArrayOf(-android.R.attr.state_pressed, -android.R.attr.state_focused), + intArrayOf() ) val colors = intArrayOf( - resolveColor(R.attr.colorControlNormal), - resolveColor(R.attr.colorControlNormal), - color + resolveColor(R.attr.colorControlNormal), + resolveColor(R.attr.colorControlNormal), + color ) return ColorStateList(states, colors) } @@ -254,4 +287,4 @@ fun Toolbar.tint(@ColorInt color: Int, tintTitle: Boolean = true) { setSubtitleTextColor(color) } (0 until childCount).asSequence().forEach { (getChildAt(it) as? ImageButton)?.setColorFilter(color) } -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt index 1eeac1a..9dca16c 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Const.kt @@ -1,6 +1,21 @@ +/* + * 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.utils -import android.support.v4.widget.ViewDragHelper +import androidx.customview.widget.ViewDragHelper /** * Created by Allan Wang on 2017-06-08. @@ -20,4 +35,4 @@ const val KAU_COLLAPSING = 1 const val KAU_EXPANDING = 2 const val KAU_EXPANDED = 3 -const val KAU_ELLIPSIS = '\u2026'
\ No newline at end of file +const val KAU_ELLIPSIS = '\u2026' 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 6568bf2..82d5608 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ContextUtils.kt @@ -1,3 +1,18 @@ +/* + * 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. + * 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. + */ @file:Suppress("NOTHING_TO_INLINE") package ca.allanwang.kau.utils @@ -11,20 +26,35 @@ import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.net.Uri import android.os.Bundle -import android.support.annotation.* -import android.support.v4.content.ContextCompat +import android.os.Looper import android.util.TypedValue import android.view.View import android.view.animation.AnimationUtils import android.widget.Toast +import androidx.annotation.AnimRes +import androidx.annotation.AttrRes +import androidx.annotation.BoolRes +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.DimenRes +import androidx.annotation.DrawableRes +import androidx.annotation.IntegerRes +import androidx.annotation.InterpolatorRes +import androidx.annotation.PluralsRes +import androidx.annotation.StringRes +import androidx.core.content.ContextCompat import ca.allanwang.kau.R import ca.allanwang.kau.logging.KL +import com.afollestad.materialdialogs.DialogBehavior import com.afollestad.materialdialogs.MaterialDialog - +import com.afollestad.materialdialogs.ModalDialog /** * Created by Allan Wang on 2017-06-03. */ +fun Context.runOnUiThread(f: Context.() -> Unit) { + if (ContextHelper.looper === Looper.myLooper()) f() else ContextHelper.handler.post { f() } +} /** * Helper class to launch an activity from a context @@ -33,19 +63,22 @@ import com.afollestad.materialdialogs.MaterialDialog */ @Suppress("DEPRECATION") inline fun <reified T : Activity> Context.startActivity( - clearStack: Boolean = false, - bundleBuilder: Bundle.() -> Unit = {}, - intentBuilder: Intent.() -> Unit = {} + clearStack: Boolean = false, + bundleBuilder: Bundle.() -> Unit = {}, + intentBuilder: Intent.() -> Unit = {} ) = startActivity(T::class.java, clearStack, bundleBuilder, intentBuilder) -@Deprecated("Use reified generic instead of passing class", - ReplaceWith("startActivity<T>(clearStack, bundleBuilder, intentBuilder)"), - DeprecationLevel.WARNING) +@Deprecated( + "Use reified generic instead of passing class", + ReplaceWith("startActivity<T>(clearStack, bundleBuilder, intentBuilder)"), + DeprecationLevel.WARNING +) inline fun <T : Activity> Context.startActivity( - clazz: Class<T>, - clearStack: Boolean = false, - bundleBuilder: Bundle.() -> Unit = {}, - intentBuilder: Intent.() -> Unit = {}) { + clazz: Class<T>, + clearStack: Boolean = false, + bundleBuilder: Bundle.() -> Unit = {}, + intentBuilder: Intent.() -> Unit = {} +) { val intent = Intent(this, clazz) if (clearStack) intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK) intent.intentBuilder() @@ -55,7 +88,6 @@ inline fun <T : Activity> Context.startActivity( if (clearStack && this is Activity) finish() } - fun Context.startPlayStoreLink(@StringRes packageIdRes: Int) = startPlayStoreLink(string(packageIdRes)) fun Context.startPlayStoreLink(packageId: String) { @@ -82,11 +114,14 @@ fun Context.startLink(vararg url: String?) { fun Context.startLink(@StringRes url: Int) = startLink(string(url)) //Toast helpers -inline fun View.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = context.toast(id, duration, log) +inline fun View.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = + context.toast(id, duration, log) -inline fun Context.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = toast(this.string(id), duration, log) +inline fun Context.toast(@StringRes id: Int, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = + toast(this.string(id), duration, log) -inline fun View.toast(text: String, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = context.toast(text, duration, log) +inline fun View.toast(text: String, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) = + context.toast(text, duration, log) inline fun Context.toast(text: String, duration: Int = Toast.LENGTH_LONG, log: Boolean = false) { Toast.makeText(this, text, duration).show() @@ -98,24 +133,33 @@ const val INVALID_ID = 0 //Resource retrievers inline fun Context.string(@StringRes id: Int): String = getString(id) -inline fun Context.string(@StringRes id: Int, fallback: String?): String? = if (id != INVALID_ID) string(id) else fallback -inline fun Context.string(@StringRes id: Int, fallback: () -> String?): String? = if (id != INVALID_ID) string(id) else fallback() +inline fun Context.string(@StringRes id: Int, fallback: String?): String? = + if (id != INVALID_ID) string(id) else fallback + +inline fun Context.string(@StringRes id: Int, fallback: () -> String?): String? = + if (id != INVALID_ID) string(id) else fallback() + inline fun Context.color(@ColorRes id: Int): Int = ContextCompat.getColor(this, id) inline fun Context.boolean(@BoolRes id: Int): Boolean = resources.getBoolean(id) inline fun Context.integer(@IntegerRes id: Int): Int = resources.getInteger(id) inline fun Context.dimen(@DimenRes id: Int): Float = resources.getDimension(id) inline fun Context.dimenPixelSize(@DimenRes id: Int): Int = resources.getDimensionPixelSize(id) inline fun Context.drawable(@DrawableRes id: Int): Drawable = ContextCompat.getDrawable(this, id) - ?: throw KauException("Drawable with id $id not found") + ?: throw KauException("Drawable with id $id not found") + +inline fun Context.drawable(@DrawableRes id: Int, fallback: Drawable?): Drawable? = + if (id != INVALID_ID) drawable(id) else fallback + +inline fun Context.drawable(@DrawableRes id: Int, fallback: () -> Drawable?): Drawable? = + if (id != INVALID_ID) drawable(id) else fallback() -inline fun Context.drawable(@DrawableRes id: Int, fallback: Drawable?): Drawable? = if (id != INVALID_ID) drawable(id) else fallback -inline fun Context.drawable(@DrawableRes id: Int, fallback: () -> Drawable?): Drawable? = if (id != INVALID_ID) drawable(id) else fallback() inline fun Context.interpolator(@InterpolatorRes id: Int) = AnimationUtils.loadInterpolator(this, id)!! inline fun Context.animation(@AnimRes id: Int) = AnimationUtils.loadAnimation(this, id)!! /** * Returns plural form of res. The quantity is also passed to the formatter as an int */ -inline fun Context.plural(@PluralsRes id: Int, quantity: Number) = resources.getQuantityString(id, quantity.toInt(), quantity.toInt())!! +inline fun Context.plural(@PluralsRes id: Int, quantity: Number) = + resources.getQuantityString(id, quantity.toInt(), quantity.toInt()) //Attr retrievers fun Context.resolveColor(@AttrRes attr: Int, @ColorInt fallback: Int = 0): Int { @@ -151,20 +195,24 @@ fun Context.resolveString(@AttrRes attr: Int, fallback: String = ""): String { } /** - * Wrapper function for the MaterialDialog adapterBuilder - * There is no need to call build() or show() as those are done by default + * Wrapper function for MaterialDialog + * + * Mainly handles invalid creations, such as showing a dialog when an activity is finishing + * See https://github.com/afollestad/material-dialogs/issues/1778 */ -inline fun Context.materialDialog(action: MaterialDialog.Builder.() -> Unit): MaterialDialog { - val builder = MaterialDialog.Builder(this) - builder.action() +inline fun Context.materialDialog( + dialogBehavior: DialogBehavior = ModalDialog, + action: MaterialDialog.() -> Unit +) { + val dialog = MaterialDialog(this, dialogBehavior) if (isFinishing) { - KL.d { "Material Dialog triggered from finishing context; did not show" } - return builder.build() + return KL.d { "Material Dialog triggered from finishing context; did not show" } } - return builder.show() + dialog.show(action) } -fun Context.getDip(value: Float): Float = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics) +fun Context.getDip(value: Float): Float = + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, resources.displayMetrics) inline val Context.isRtl: Boolean get() = resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL @@ -181,7 +229,10 @@ inline val Context.isNavBarOnBottom: Boolean return !canMove || dm.widthPixels < dm.heightPixels } -fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || ContextCompat.checkSelfPermission(this, permissions) == PackageManager.PERMISSION_GRANTED +fun Context.hasPermission(permissions: String) = !buildIsMarshmallowAndUp || ContextCompat.checkSelfPermission( + this, + permissions +) == PackageManager.PERMISSION_GRANTED fun Context.copyToClipboard(text: String?, label: String = "Copied Text", showToast: Boolean = true) { val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager @@ -207,4 +258,4 @@ fun Context.shareText(text: String?) { * As of now, it is only checked when tied to an activity */ inline val Context.isFinishing: Boolean - get() = (this as? Activity)?.isFinishing ?: false
\ No newline at end of file + get() = (this as? Activity)?.isFinishing ?: false diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt new file mode 100644 index 0000000..57a9921 --- /dev/null +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/CoroutineUtils.kt @@ -0,0 +1,75 @@ +/* + * 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.utils + +import android.content.Context +import android.os.Handler +import android.os.Looper +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineStart +import kotlinx.coroutines.android.asCoroutineDispatcher +import kotlinx.coroutines.async +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +object ContextHelper : CoroutineScope { + + val looper = Looper.getMainLooper() + + val handler = Handler(looper) + + /** + * Creating dispatcher from main handler to avoid IO + * See https://github.com/Kotlin/kotlinx.coroutines/issues/878 + */ + val dispatcher = handler.asCoroutineDispatcher("kau-main") + + override val coroutineContext: CoroutineContext get() = dispatcher +} + +/** + * 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 + +/** + * Calls [launch] with an explicit dispatcher for Android's main thread + */ +fun CoroutineScope.launchMain( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = launch(ContextHelper.dispatcher + context, start, block) + +/** + * Calls [async] with an explicit dispatcher for Android's main thread + */ +fun CoroutineScope.asyncMain( + context: CoroutineContext = EmptyCoroutineContext, + start: CoroutineStart = CoroutineStart.DEFAULT, + block: suspend CoroutineScope.() -> Unit +) = async(ContextHelper.dispatcher + context, start, block) + +/** + * Calls [withContext] with an explicit dispatcher for Android's main thread + */ +suspend fun <T> withMainContext(block: suspend CoroutineScope.() -> T) = + withContext(ContextHelper.dispatcher, block) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/DrawableUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/DrawableUtils.kt index dae3bff..6950f2f 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/DrawableUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/DrawableUtils.kt @@ -1,9 +1,24 @@ +/* + * 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.utils import android.content.res.ColorStateList import android.graphics.drawable.Drawable -import android.support.annotation.ColorInt -import android.support.v4.graphics.drawable.DrawableCompat +import androidx.annotation.ColorInt +import androidx.core.graphics.drawable.DrawableCompat /** * Wrap the color into a state and tint the drawable @@ -17,4 +32,4 @@ fun Drawable.tint(state: ColorStateList): Drawable { val drawable = DrawableCompat.wrap(mutate()) DrawableCompat.setTintList(drawable, state) return drawable -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt index bfbc009..1d27bfc 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/FileUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import java.io.File @@ -6,4 +21,5 @@ import java.io.InputStream /** * Created by Allan Wang on 2017-08-04. */ -fun File.copyFromInputStream(inputStream: InputStream) = inputStream.use { input -> outputStream().use { output -> input.copyTo(output) } }
\ No newline at end of file +fun File.copyFromInputStream(inputStream: InputStream) = + inputStream.use { input -> outputStream().use { output -> input.copyTo(output) } } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt index 1db7694..064d70b 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/FontUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.content.Context @@ -14,17 +29,17 @@ object FontUtils { synchronized(sTypefaceCache) { if (!sTypefaceCache.containsKey(font)) { val tf = Typeface.createFromAsset( - context.applicationContext.assets, "fonts/$font.ttf") + context.applicationContext.assets, "fonts/$font.ttf" + ) sTypefaceCache.put(font, tf) } return sTypefaceCache.get(font) - ?: throw IllegalArgumentException("Font error; typeface does not exist at assets/fonts$font.ttf") + ?: throw IllegalArgumentException("Font error; typeface does not exist at assets/fonts$font.ttf") } } fun getName(typeface: Typeface): String? = sTypefaceCache.entries.firstOrNull { it.value == typeface }?.key - } fun Context.getFont(font: String) = FontUtils.get(this, font) -fun Context.getFontName(typeface: Typeface) = FontUtils.getName(typeface)
\ No newline at end of file +fun Context.getFontName(typeface: Typeface) = FontUtils.getName(typeface) 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 acc71f2..75dc1c1 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/FragmentUtils.kt @@ -1,12 +1,26 @@ +/* + * 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.utils -import android.support.v4.app.Fragment -import org.jetbrains.anko.bundleOf +import androidx.fragment.app.Fragment /** * 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 -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt index 51691af..8b40352 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/IIconUtils.kt @@ -1,10 +1,25 @@ +/* + * 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.utils import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.graphics.drawable.Drawable -import android.support.annotation.ColorInt +import androidx.annotation.ColorInt import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon @@ -12,10 +27,15 @@ import com.mikepenz.iconics.typeface.IIcon * Created by Allan Wang on 2017-05-29. */ @KauUtils -fun IIcon.toDrawable(c: Context, sizeDp: Int = 24, @ColorInt color: Int = Color.WHITE, builder: IconicsDrawable.() -> Unit = {}): Drawable { +fun IIcon.toDrawable( + c: Context, + sizeDp: Int = 24, + @ColorInt color: Int = Color.WHITE, + builder: IconicsDrawable.() -> Unit = {} +): Drawable { val state = ColorStateList.valueOf(color) val icon = IconicsDrawable(c).icon(this).color(state) if (sizeDp > 0) icon.sizeDp(sizeDp) icon.builder() return icon -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt index 8c7c039..8765c69 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Kotterknife.kt @@ -1,4 +1,19 @@ -@file:Suppress("UNCHECKED_CAST") +/* + * 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. + */ +@file:Suppress("UNCHECKED_CAST", "DEPRECATION") package ca.allanwang.kau.utils @@ -8,148 +23,89 @@ package ca.allanwang.kau.utils * Courtesy of Jake Wharton * * https://github.com/JakeWharton/kotterknife/blob/master/src/main/kotlin/kotterknife/ButterKnife.kt + * + * Note that while this is useful for binding ids, there also exists other alternatives, such as + * `kotlin-android-extensions`. + * + * For fragments, make sure that the views are reset after the fragment lifecycle. */ import android.app.Activity import android.app.Dialog import android.app.DialogFragment import android.app.Fragment -import android.support.v7.widget.RecyclerView.ViewHolder import android.view.View -import java.util.* +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ViewHolder +import java.util.Collections +import java.util.WeakHashMap import kotlin.properties.ReadOnlyProperty import kotlin.reflect.KProperty -import android.support.v4.app.DialogFragment as SupportDialogFragment -import android.support.v4.app.Fragment as SupportFragment - -private const val DEPRECATION_MESSAGE = "Kotterknife will be removed in favour of the kotlin_android_extensions plugin" +import androidx.fragment.app.DialogFragment as SupportDialogFragment +import androidx.fragment.app.Fragment as SupportFragment -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindView(id: Int) - : ReadOnlyProperty<View, V> = required(id, viewFinder) +fun <V : View> View.bindView(id: Int): ReadOnlyProperty<View, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindView(id: Int) - : ReadOnlyProperty<Activity, V> = required(id, viewFinder) +fun <V : View> Activity.bindView(id: Int): ReadOnlyProperty<Activity, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindView(id: Int) - : ReadOnlyProperty<Dialog, V> = required(id, viewFinder) +fun <V : View> Dialog.bindView(id: Int): ReadOnlyProperty<Dialog, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindView(id: Int) - : ReadOnlyProperty<DialogFragment, V> = required(id, viewFinder) +fun <V : View> DialogFragment.bindView(id: Int): ReadOnlyProperty<DialogFragment, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindView(id: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, V> = required(id, viewFinder) +fun <V : View> SupportDialogFragment.bindView(id: Int): ReadOnlyProperty<SupportDialogFragment, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindView(id: Int) - : ReadOnlyProperty<Fragment, V> = required(id, viewFinder) +fun <V : View> Fragment.bindView(id: Int): ReadOnlyProperty<Fragment, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindView(id: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, V> = required(id, viewFinder) +fun <V : View> SupportFragment.bindView(id: Int): ReadOnlyProperty<SupportFragment, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindView(id: Int) - : ReadOnlyProperty<ViewHolder, V> = required(id, viewFinder) +fun <V : View> RecyclerView.ViewHolder.bindView(id: Int): ReadOnlyProperty<RecyclerView.ViewHolder, V> = required(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindOptionalView(id: Int) - : ReadOnlyProperty<View, V?> = optional(id, viewFinder) +fun <V : View> View.bindOptionalView(id: Int): ReadOnlyProperty<View, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindOptionalView(id: Int) - : ReadOnlyProperty<Activity, V?> = optional(id, viewFinder) +fun <V : View> Activity.bindOptionalView(id: Int): ReadOnlyProperty<Activity, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindOptionalView(id: Int) - : ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder) +fun <V : View> Dialog.bindOptionalView(id: Int): ReadOnlyProperty<Dialog, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindOptionalView(id: Int) - : ReadOnlyProperty<DialogFragment, V?> = optional(id, viewFinder) +fun <V : View> DialogFragment.bindOptionalView(id: Int): ReadOnlyProperty<DialogFragment, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindOptionalView(id: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, V?> = optional(id, viewFinder) +fun <V : View> SupportDialogFragment.bindOptionalView(id: Int): ReadOnlyProperty<SupportDialogFragment, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindOptionalView(id: Int) - : ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder) +fun <V : View> Fragment.bindOptionalView(id: Int): ReadOnlyProperty<Fragment, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindOptionalView(id: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, V?> = optional(id, viewFinder) +fun <V : View> SupportFragment.bindOptionalView(id: Int): ReadOnlyProperty<SupportFragment, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindOptionalView(id: Int) - : ReadOnlyProperty<ViewHolder, V?> = optional(id, viewFinder) +fun <V : View> RecyclerView.ViewHolder.bindOptionalView(id: Int): ReadOnlyProperty<RecyclerView.ViewHolder, V?> = optional(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindViews(vararg ids: Int) - : ReadOnlyProperty<View, List<V>> = required(ids, viewFinder) +fun <V : View> View.bindViews(vararg ids: Int): ReadOnlyProperty<View, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindViews(vararg ids: Int) - : ReadOnlyProperty<Activity, List<V>> = required(ids, viewFinder) +fun <V : View> Activity.bindViews(vararg ids: Int): ReadOnlyProperty<Activity, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindViews(vararg ids: Int) - : ReadOnlyProperty<Dialog, List<V>> = required(ids, viewFinder) +fun <V : View> Dialog.bindViews(vararg ids: Int): ReadOnlyProperty<Dialog, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindViews(vararg ids: Int) - : ReadOnlyProperty<DialogFragment, List<V>> = required(ids, viewFinder) +fun <V : View> DialogFragment.bindViews(vararg ids: Int): ReadOnlyProperty<DialogFragment, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindViews(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = required(ids, viewFinder) +fun <V : View> SupportDialogFragment.bindViews(vararg ids: Int): ReadOnlyProperty<SupportDialogFragment, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindViews(vararg ids: Int) - : ReadOnlyProperty<Fragment, List<V>> = required(ids, viewFinder) +fun <V : View> Fragment.bindViews(vararg ids: Int): ReadOnlyProperty<Fragment, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindViews(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = required(ids, viewFinder) +fun <V : View> SupportFragment.bindViews(vararg ids: Int): ReadOnlyProperty<SupportFragment, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindViews(vararg ids: Int) - : ReadOnlyProperty<ViewHolder, List<V>> = required(ids, viewFinder) +fun <V : View> ViewHolder.bindViews(vararg ids: Int): ReadOnlyProperty<ViewHolder, List<V>> = required(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<View, List<V>> = optional(ids, viewFinder) +fun <V : View> View.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<View, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<Activity, List<V>> = optional(ids, viewFinder) +fun <V : View> Activity.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<Activity, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<Dialog, List<V>> = optional(ids, viewFinder) +fun <V : View> Dialog.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<Dialog, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<DialogFragment, List<V>> = optional(ids, viewFinder) +fun <V : View> DialogFragment.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<DialogFragment, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = optional(ids, viewFinder) +fun <V : View> SupportDialogFragment.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<SupportDialogFragment, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<Fragment, List<V>> = optional(ids, viewFinder) +fun <V : View> Fragment.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<Fragment, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = optional(ids, viewFinder) +fun <V : View> SupportFragment.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<SupportFragment, List<V>> = optional(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindOptionalViews(vararg ids: Int) - : ReadOnlyProperty<ViewHolder, List<V>> = optional(ids, viewFinder) +fun <V : View> ViewHolder.bindOptionalViews(vararg ids: Int): ReadOnlyProperty<ViewHolder, List<V>> = optional(ids, viewFinder) private inline val View.viewFinder: View.(Int) -> View? get() = { findViewById(it) } @@ -162,14 +118,14 @@ private inline val DialogFragment.viewFinder: DialogFragment.(Int) -> View? private inline val SupportDialogFragment.viewFinder: SupportDialogFragment.(Int) -> View? get() = { dialog.findViewById(it) } private inline val Fragment.viewFinder: Fragment.(Int) -> View? - get() = { view.findViewById(it) } + get() = { view!!.findViewById(it) } private inline val SupportFragment.viewFinder: SupportFragment.(Int) -> View? get() = { view!!.findViewById(it) } private inline val ViewHolder.viewFinder: ViewHolder.(Int) -> View? get() = { itemView.findViewById(it) } private fun viewNotFound(id: Int, desc: KProperty<*>): Nothing = - throw IllegalStateException("View ID $id for '${desc.name}' not found.") + throw IllegalStateException("View ID $id for '${desc.name}' not found.") private fun <T, V : View> required(id: Int, finder: T.(Int) -> View?) = Lazy { t: T, desc -> (t.finder(id) as V?)?.apply { } ?: viewNotFound(id, desc) @@ -183,7 +139,8 @@ private fun <T, V : View> required(ids: IntArray, finder: T.(Int) -> View?) = La } } -private fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?) = Lazy { t: T, _ -> ids.map { t.finder(it) as V? }.filterNotNull() } +private fun <T, V : View> optional(ids: IntArray, finder: T.(Int) -> View?) = + Lazy { t: T, _ -> ids.map { t.finder(it) as V? }.filterNotNull() } // Like Kotlin's lazy delegate but the initializer gets the target and metadata passed to it private open class Lazy<in T, out V>(private val initializer: (T, KProperty<*>) -> V) : ReadOnlyProperty<T, V> { @@ -209,139 +166,76 @@ private open class Lazy<in T, out V>(private val initializer: (T, KProperty<*>) * Credits to <a href="https://github.com/MichaelRocks">MichaelRocks</a> */ -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindViewResettable(id: Int) - : ReadOnlyProperty<View, V> = requiredResettable(id, viewFinder) +fun <V : View> View.bindViewResettable(id: Int): ReadOnlyProperty<View, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindViewResettable(id: Int) - : ReadOnlyProperty<Activity, V> = requiredResettable(id, viewFinder) +fun <V : View> Activity.bindViewResettable(id: Int): ReadOnlyProperty<Activity, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindViewResettable(id: Int) - : ReadOnlyProperty<Dialog, V> = requiredResettable(id, viewFinder) +fun <V : View> Dialog.bindViewResettable(id: Int): ReadOnlyProperty<Dialog, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindViewResettable(id: Int) - : ReadOnlyProperty<DialogFragment, V> = requiredResettable(id, viewFinder) +fun <V : View> DialogFragment.bindViewResettable(id: Int): ReadOnlyProperty<DialogFragment, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindViewResettable(id: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, V> = requiredResettable(id, viewFinder) +fun <V : View> SupportDialogFragment.bindViewResettable(id: Int): ReadOnlyProperty<SupportDialogFragment, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindViewResettable(id: Int) - : ReadOnlyProperty<Fragment, V> = requiredResettable(id, viewFinder) +fun <V : View> Fragment.bindViewResettable(id: Int): ReadOnlyProperty<Fragment, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindViewResettable(id: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, V> = requiredResettable(id, viewFinder) +fun <V : View> SupportFragment.bindViewResettable(id: Int): ReadOnlyProperty<SupportFragment, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindViewResettable(id: Int) - : ReadOnlyProperty<ViewHolder, V> = requiredResettable(id, viewFinder) +fun <V : View> ViewHolder.bindViewResettable(id: Int): ReadOnlyProperty<ViewHolder, V> = requiredResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<View, V?> = optionalResettable(id, viewFinder) +fun <V : View> View.bindOptionalViewResettable(id: Int): ReadOnlyProperty<View, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<Activity, V?> = optionalResettable(id, viewFinder) +fun <V : View> Activity.bindOptionalViewResettable(id: Int): ReadOnlyProperty<Activity, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<Dialog, V?> = optionalResettable(id, viewFinder) +fun <V : View> Dialog.bindOptionalViewResettable(id: Int): ReadOnlyProperty<Dialog, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<DialogFragment, V?> = optionalResettable(id, viewFinder) +fun <V : View> DialogFragment.bindOptionalViewResettable(id: Int): ReadOnlyProperty<DialogFragment, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, V?> = optionalResettable(id, viewFinder) +fun <V : View> SupportDialogFragment.bindOptionalViewResettable(id: Int): ReadOnlyProperty<SupportDialogFragment, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<Fragment, V?> = optionalResettable(id, viewFinder) +fun <V : View> Fragment.bindOptionalViewResettable(id: Int): ReadOnlyProperty<Fragment, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, V?> = optionalResettable(id, viewFinder) +fun <V : View> SupportFragment.bindOptionalViewResettable(id: Int): ReadOnlyProperty<SupportFragment, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindOptionalViewResettable(id: Int) - : ReadOnlyProperty<ViewHolder, V?> = optionalResettable(id, viewFinder) +fun <V : View> ViewHolder.bindOptionalViewResettable(id: Int): ReadOnlyProperty<ViewHolder, V?> = optionalResettable(id, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<View, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> View.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<View, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Activity, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> Activity.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<Activity, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Dialog, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> Dialog.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<Dialog, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<DialogFragment, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> DialogFragment.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<DialogFragment, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> SupportDialogFragment.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<SupportDialogFragment, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Fragment, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> Fragment.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<Fragment, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> SupportFragment.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<SupportFragment, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindViewsResettable(vararg ids: Int) - : ReadOnlyProperty<ViewHolder, List<V>> = requiredResettable(ids, viewFinder) +fun <V : View> ViewHolder.bindViewsResettable(vararg ids: Int): ReadOnlyProperty<ViewHolder, List<V>> = requiredResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> View.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<View, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> View.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<View, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Activity.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Activity, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> Activity.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<Activity, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Dialog.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Dialog, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> Dialog.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<Dialog, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> DialogFragment.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<DialogFragment, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> DialogFragment.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<DialogFragment, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportDialogFragment.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.DialogFragment, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> SupportDialogFragment.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<SupportDialogFragment, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> Fragment.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<Fragment, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> Fragment.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<Fragment, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> SupportFragment.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<android.support.v4.app.Fragment, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> SupportFragment.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<SupportFragment, List<V>> = optionalResettable(ids, viewFinder) -@Deprecated(DEPRECATION_MESSAGE) -fun <V : View> ViewHolder.bindOptionalViewsResettable(vararg ids: Int) - : ReadOnlyProperty<ViewHolder, List<V>> = optionalResettable(ids, viewFinder) +fun <V : View> ViewHolder.bindOptionalViewsResettable(vararg ids: Int): ReadOnlyProperty<RecyclerView.ViewHolder, List<V>> = optionalResettable(ids, viewFinder) private fun <T, V : View> requiredResettable(id: Int, finder: T.(Int) -> View?) = LazyResettable { t: T, desc -> (t.finder(id) as V?)?.apply { } ?: viewNotFound(id, desc) } -private fun <T, V : View> optionalResettable(id: Int, finder: T.(Int) -> View?) = LazyResettable { t: T, _ -> t.finder(id) as V? } +private fun <T, V : View> optionalResettable(id: Int, finder: T.(Int) -> View?) = + LazyResettable { t: T, _ -> t.finder(id) as V? } private fun <T, V : View> requiredResettable(ids: IntArray, finder: T.(Int) -> View?) = LazyResettable { t: T, desc -> ids.map { @@ -349,7 +243,8 @@ private fun <T, V : View> requiredResettable(ids: IntArray, finder: T.(Int) -> V } } -private fun <T, V : View> optionalResettable(ids: IntArray, finder: T.(Int) -> View?) = LazyResettable { t: T, _ -> ids.map { t.finder(it) as V? }.filterNotNull() } +private fun <T, V : View> optionalResettable(ids: IntArray, finder: T.(Int) -> View?) = + LazyResettable { t: T, _ -> ids.map { t.finder(it) as V? }.filterNotNull() } //Like Kotterknife's lazy delegate but is resettable private class LazyResettable<in T, out V>(initializer: (T, KProperty<*>) -> V) : Lazy<T, V>(initializer) { @@ -363,7 +258,6 @@ private class LazyResettable<in T, out V>(initializer: (T, KProperty<*>) -> V) : } } -@Deprecated(DEPRECATION_MESSAGE) object Kotterknife { fun reset(target: Any) { KotterknifeRegistry.reset(target) @@ -373,7 +267,8 @@ object Kotterknife { private object KotterknifeRegistry { private val lazyMap = WeakHashMap<Any, MutableCollection<LazyResettable<*, *>>>() - fun register(target: Any, lazy: LazyResettable<*, *>) = lazyMap.getOrPut(target, { Collections.newSetFromMap(WeakHashMap()) }).add(lazy) + fun register(target: Any, lazy: LazyResettable<*, *>) = + lazyMap.getOrPut(target, { Collections.newSetFromMap(WeakHashMap()) }).add(lazy) fun reset(target: Any) = lazyMap[target]?.forEach(LazyResettable<*, *>::reset) -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/NetworkUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/NetworkUtils.kt index 2271c16..32cf084 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/NetworkUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/NetworkUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.annotation.SuppressLint @@ -7,6 +22,7 @@ import android.net.ConnectivityManager /** * Created by Allan Wang on 2017-07-07. */ +@Deprecated("Applications should make use of network callbacks instead of individual queries") inline val Context.isNetworkAvailable: Boolean @SuppressLint("MissingPermission") get() { @@ -15,6 +31,7 @@ inline val Context.isNetworkAvailable: Boolean return activeNetworkInfo?.isConnectedOrConnecting ?: false } +@Deprecated("Applications should make use of network callbacks instead of individual queries") inline val Context.isWifiConnected: Boolean @SuppressLint("MissingPermission") get() { @@ -23,10 +40,11 @@ inline val Context.isWifiConnected: Boolean return (activeNetworkInfo?.type ?: -1) == ConnectivityManager.TYPE_WIFI } +@Deprecated("Applications should make use of network callbacks instead of individual queries") inline val Context.isMobileDataConnected: Boolean @SuppressLint("MissingPermission") get() { val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val activeNetworkInfo = connectivityManager.activeNetworkInfo return (activeNetworkInfo?.type ?: -1) == ConnectivityManager.TYPE_MOBILE - }
\ No newline at end of file + } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/NotificationUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/NotificationUtils.kt index 1eb0076..126b133 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/NotificationUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/NotificationUtils.kt @@ -1,10 +1,24 @@ +/* + * 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.utils import android.content.Context -import android.support.v4.app.NotificationManagerCompat - +import androidx.core.app.NotificationManagerCompat /** * Created by Allan Wang on 2017-08-04. */ -fun Context.cancelNotification(notifId: Int) = NotificationManagerCompat.from(this).cancel(notifId)
\ No newline at end of file +fun Context.cancelNotification(notifId: Int) = NotificationManagerCompat.from(this).cancel(notifId) diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt index 77750d3..4055847 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/PackageUtils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.content.ActivityNotFoundException @@ -70,4 +85,4 @@ inline val Context.isFromGooglePlay: Boolean get() { val installer = installerPackageName return arrayOf(INSTALLER_GOOGLE_PLAY_FEEDBACK, INSTALLER_GOOGLE_PLAY_VENDING).any { it == installer } - }
\ No newline at end of file + } diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt index 3a34db5..11494b3 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/RecyclerUtils.kt @@ -1,8 +1,23 @@ +/* + * 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.utils import android.graphics.Rect -import android.support.v7.widget.RecyclerView import android.view.View +import androidx.recyclerview.widget.RecyclerView /** * Created by Allan Wang on 2017-07-11. @@ -22,4 +37,4 @@ class MarginItemDecoration(sizeDp: Int, val edgeFlags: Int) : RecyclerView.ItemD if (edgeFlags and KAU_RIGHT > 0) outRect.right += sizePx if (edgeFlags and KAU_BOTTOM > 0) outRect.bottom += sizePx } -}
\ No newline at end of file +} diff --git a/core/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt b/core/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt index ec206ee..523a586 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/TransitionUtils.kt @@ -1,14 +1,29 @@ +/* + * 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.utils import android.os.Build -import android.support.annotation.RequiresApi -import android.support.annotation.TransitionRes -import android.support.transition.AutoTransition -import android.support.transition.TransitionInflater -import android.support.transition.TransitionManager import android.transition.Transition import android.view.ViewGroup -import android.support.transition.Transition as SupportTransition +import androidx.annotation.RequiresApi +import androidx.annotation.TransitionRes +import androidx.transition.AutoTransition +import androidx.transition.TransitionInflater +import androidx.transition.TransitionManager +import androidx.transition.Transition as SupportTransition /** * Created by Allan Wang on 2017-06-24. @@ -29,7 +44,8 @@ fun Transition.addEndListener(onEnd: (transition: Transition) -> Unit) { } @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -class SupportTransitionEndListener(val onEnd: (transition: SupportTransition) -> Unit) : SupportTransition.TransitionListener { +class SupportTransitionEndListener(val onEnd: (transition: SupportTransition) -> Unit) : + SupportTransition.TransitionListener { override fun onTransitionEnd(transition: SupportTransition) = onEnd(transition) override fun onTransitionResume(transition: SupportTransition) {} override fun onTransitionPause(transition: SupportTransition) {} @@ -52,9 +68,9 @@ fun ViewGroup.transitionAuto(builder: AutoTransition.() -> Unit = {}) { } @KauUtils -fun ViewGroup.transitionDelayed(@TransitionRes id: Int, builder: android.support.transition.Transition.() -> Unit = {}) { +fun ViewGroup.transitionDelayed(@TransitionRes id: Int, builder: androidx.transition.Transition.() -> Unit = {}) { if (!buildIsLollipopAndUp) return val transition = TransitionInflater.from(context).inflateTransition(id) transition.builder() TransitionManager.beginDelayedTransition(this, transition) -}
\ 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 46e29b2..48a2028 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/Utils.kt @@ -1,3 +1,18 @@ +/* + * 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.utils import android.content.Context @@ -8,12 +23,11 @@ import android.graphics.drawable.BitmapDrawable import android.graphics.drawable.Drawable import android.os.Handler import android.os.Looper -import android.support.annotation.IntRange +import androidx.annotation.IntRange import ca.allanwang.kau.R import java.math.RoundingMode import java.text.DecimalFormat - /** * Created by Allan Wang on 2017-05-28. */ @@ -127,10 +141,10 @@ fun postDelayed(delay: Long, action: () -> Unit) { } inline val kauIsMainThread: Boolean - get() = Looper.myLooper() == Looper.getMainLooper() + get() = Looper.myLooper() == ContextHelper.looper class KauException(message: String) : RuntimeException(message) fun String.withMaxLength(n: Int): String = - if (length <= n) this - else substring(0, n - 1) + KAU_ELLIPSIS
\ No newline at end of file + if (length <= n) this + else substring(0, n - 1) + KAU_ELLIPSIS 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 4e020bc..186d125 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/utils/ViewUtils.kt @@ -1,3 +1,18 @@ +/* + * 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. + */ @file:Suppress("NOTHING_TO_INLINE") package ca.allanwang.kau.utils @@ -7,15 +22,6 @@ import android.annotation.SuppressLint import android.content.Context import android.graphics.Color import android.os.Build -import android.support.annotation.ColorInt -import android.support.annotation.ColorRes -import android.support.annotation.RequiresApi -import android.support.annotation.StringRes -import android.support.design.widget.FloatingActionButton -import android.support.design.widget.Snackbar -import android.support.design.widget.TextInputEditText -import android.support.v7.widget.LinearLayoutManager -import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.MotionEvent import android.view.View @@ -23,11 +29,19 @@ import android.view.ViewGroup import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.ImageView +import androidx.annotation.ColorInt +import androidx.annotation.ColorRes +import androidx.annotation.RequiresApi +import androidx.annotation.StringRes +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import ca.allanwang.kau.ui.createSimpleRippleDrawable +import com.google.android.material.floatingactionbutton.FloatingActionButton +import com.google.android.material.snackbar.Snackbar +import com.google.android.material.textfield.TextInputEditText import com.mikepenz.iconics.IconicsDrawable import com.mikepenz.iconics.typeface.IIcon - /** * Created by Allan Wang on 2017-05-31. */ @@ -81,10 +95,16 @@ fun View.snackbar(text: String, duration: Int = Snackbar.LENGTH_LONG, builder: S return snackbar } -fun View.snackbar(@StringRes textId: Int, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {}) = snackbar(context.string(textId), duration, builder) +fun View.snackbar(@StringRes textId: Int, duration: Int = Snackbar.LENGTH_LONG, builder: Snackbar.() -> Unit = {}) = + snackbar(context.string(textId), duration, builder) @KauUtils -fun ImageView.setIcon(icon: IIcon?, sizeDp: Int = 24, @ColorInt color: Int = Color.WHITE, builder: IconicsDrawable.() -> Unit = {}) { +fun ImageView.setIcon( + icon: IIcon?, + sizeDp: Int = 24, + @ColorInt color: Int = Color.WHITE, + builder: IconicsDrawable.() -> Unit = {} +) { if (icon == null) return setImageDrawable(icon.toDrawable(context, sizeDp = sizeDp, color = color, builder = builder)) } @@ -98,7 +118,8 @@ fun FloatingActionButton.showIf(show: Boolean) = if (show) show() else hide() fun FloatingActionButton.hideIf(hide: Boolean) = if (hide) hide() else show() @KauUtils -fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View = LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) +fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View = + LayoutInflater.from(context).inflate(layoutId, this, attachToRoot) /** * Set left margin to a value in px @@ -150,10 +171,10 @@ fun View.setMargin(margin: Int) = setMargins(margin, KAU_ALL) private fun View.setMargins(margin: Int, flag: Int): Boolean { val p = (layoutParams as? ViewGroup.MarginLayoutParams) ?: return false p.setMargins( - if (flag and KAU_LEFT > 0) margin else p.leftMargin, - if (flag and KAU_TOP > 0) margin else p.topMargin, - if (flag and KAU_RIGHT > 0) margin else p.rightMargin, - if (flag and KAU_BOTTOM > 0) margin else p.bottomMargin + if (flag and KAU_LEFT > 0) margin else p.leftMargin, + if (flag and KAU_TOP > 0) margin else p.topMargin, + if (flag and KAU_RIGHT > 0) margin else p.rightMargin, + if (flag and KAU_BOTTOM > 0) margin else p.bottomMargin ) return true } @@ -206,26 +227,31 @@ fun View.setPadding(padding: Int) = setPadding(padding, KAU_ALL) @KauUtils private fun View.setPadding(padding: Int, flag: Int) { setPadding( - if (flag and KAU_LEFT > 0) padding else paddingLeft, - if (flag and KAU_TOP > 0) padding else paddingTop, - if (flag and KAU_RIGHT > 0) padding else paddingRight, - if (flag and KAU_BOTTOM > 0) padding else paddingBottom + if (flag and KAU_LEFT > 0) padding else paddingLeft, + if (flag and KAU_TOP > 0) padding else paddingTop, + if (flag and KAU_RIGHT > 0) padding else paddingRight, + if (flag and KAU_BOTTOM > 0) padding else paddingBottom ) } @KauUtils fun View.hideKeyboard() { clearFocus() - (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(windowToken, 0) + (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow( + windowToken, + 0 + ) } @KauUtils fun View.showKeyboard() { requestFocus() - (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput(this, InputMethodManager.SHOW_IMPLICIT) + (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).showSoftInput( + this, + InputMethodManager.SHOW_IMPLICIT + ) } - @RequiresApi(Build.VERSION_CODES.LOLLIPOP) @KauUtils fun View.setRippleBackground(@ColorInt foregroundColor: Int, @ColorInt backgroundColor: Int) { @@ -243,12 +269,14 @@ 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(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 @@ -263,7 +291,11 @@ fun RecyclerView.withLinearAdapter(rvAdapter: RecyclerView.Adapter<*>) = apply { * If it is not shown, the action will be invoked directly and no further actions will be made * If it is already shown, scaling and alpha animations will be added to the action */ -inline fun <T : ImageView> T.fadeScaleTransition(duration: Long = 500L, minScale: Float = 0.7f, crossinline action: T.() -> Unit) { +inline fun <T : ImageView> T.fadeScaleTransition( + duration: Long = 500L, + minScale: Float = 0.7f, + crossinline action: T.() -> Unit +) { if (!isVisible) action() else { var transitioned = false @@ -291,7 +323,7 @@ fun FloatingActionButton.hideOnDownwardsScroll(recycler: RecyclerView) { recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - if (newState == android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE && !isShown) show() + if (newState == RecyclerView.SCROLL_STATE_IDLE && !isShown) show() } override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { @@ -324,4 +356,4 @@ inline fun View.setOnSingleTapListener(crossinline onSingleTap: (v: View, event: else -> false } } -}
\ No newline at end of file +} 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 28d51a4..2e66a97 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/xml/Changelog.kt @@ -1,39 +1,54 @@ +/* + * 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.xml import android.content.Context import android.content.res.XmlResourceParser -import android.support.annotation.ColorInt -import android.support.annotation.LayoutRes -import android.support.annotation.XmlRes -import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.TextView +import androidx.annotation.ColorInt +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 com.afollestad.materialdialogs.list.customListAdapter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.xmlpull.v1.XmlPullParser - /** * Created by Allan Wang on 2017-05-28. * * 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() - } +fun Context.showChangelog(@XmlRes xmlRes: Int, @ColorInt textColor: Int? = null, customize: MaterialDialog.() -> Unit = {}) { + ctxCoroutine.launch { + val items = withContext(Dispatchers.Default) { parse(this@showChangelog, xmlRes) } + materialDialog { + title(R.string.kau_changelog) + positiveButton(R.string.kau_great) + customListAdapter(ChangelogAdapter(items, textColor), null) + customize() } } } @@ -42,10 +57,13 @@ fun Context.showChangelog(@XmlRes xmlRes: Int, @ColorInt textColor: Int? = null, * Internals of the changelog dialog * Contains an mainAdapter for each item, as well as the tags to parse */ -internal class ChangelogAdapter(val items: List<Pair<String, ChangelogType>>, @ColorInt val textColor: Int? = null) : RecyclerView.Adapter<ChangelogAdapter.ChangelogVH>() { +internal class ChangelogAdapter(val items: List<Pair<String, ChangelogType>>, @ColorInt val textColor: Int? = null) : + RecyclerView.Adapter<ChangelogAdapter.ChangelogVH>() { - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ChangelogVH(LayoutInflater.from(parent.context) - .inflate(items[viewType].second.layout, parent, false)) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = ChangelogVH( + LayoutInflater.from(parent.context) + .inflate(items[viewType].second.layout, parent, false) + ) override fun onBindViewHolder(holder: ChangelogVH, position: Int) { holder.text.text = items[position].first @@ -98,4 +116,3 @@ internal enum class ChangelogType(val tag: String, val attr: String, @LayoutRes return true } } - 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 7caf5d6..73d7d6c 100644 --- a/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt +++ b/core/src/main/kotlin/ca/allanwang/kau/xml/FAQ.kt @@ -1,13 +1,26 @@ +/* + * 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.xml import android.content.Context import android.content.res.XmlResourceParser -import android.support.annotation.XmlRes 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 /** @@ -15,51 +28,53 @@ 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( - @XmlRes xmlRes: Int, - /** - * 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 + @XmlRes xmlRes: Int, + /** + * If \n is used, it will automatically be converted to </br> + */ + 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 "")))) - question = null - 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 "")) + ) + ) + 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)
\ No newline at end of file +data class FaqItem(val number: Int, val question: Spanned, val answer: Spanned) |