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 | |
parent | 572d470a2677eec0405a7b16ab9a2cfb954d6832 (diff) | |
parent | 879ac366074697dd0a7fbb2c3d99a48d7aeeb22d (diff) | |
download | kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.tar.gz kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.tar.bz2 kau-5d86d9089697b152192b2786fbe0c708dd8b5e2b.zip |
Merge dev
Diffstat (limited to 'core')
87 files changed, 2244 insertions, 927 deletions
diff --git a/core/README.md b/core/README.md index 39899dd..b9a10f5 100644 --- a/core/README.md +++ b/core/README.md @@ -131,6 +131,9 @@ These variants are weakly held in the private `KotterknifeRegistry` object, and values through the `Kotterknife.reset` method. This is typically useful for Fragments, as they do not follow the same lifecycle as Activities and Views. +Note that this is useful for views that have ids in multiple layout files or in `id.xml` files. +Kotlin has another solution, [`kotlin-android-extensions`](https://kotlinlang.org/docs/tutorials/android-plugin.html), which is more convenient. + ## Ripple Canvas Ripple canvas provides a way to create simultaneous ripples against a background color. @@ -210,7 +213,6 @@ Include your email and subject, along with other optional configurations such as ## Extension Functions > "[Extensions](https://kotlinlang.org/docs/reference/extensions.html) provide the ability to extend a class with new functionality without having to inherit from the class" -<br/>Note that since KAU depends on [ANKO](https://github.com/Kotlin/anko), all of the extensions in its core package is also in KAU. KAU's vast collection of extensions is one of its strongest features. There are too many to explain here, but you may check out the [utils package](https://github.com/AllanWang/KAU/tree/master/core/src/main/kotlin/ca/allanwang/kau/utils) diff --git a/core/build.gradle b/core/build.gradle index 046a734..3ac4f36 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -5,20 +5,18 @@ apply from: '../android-lib.gradle' dependencies { api kauDependency.kotlin - api "com.android.support:support-annotations:${kau.supportLibs}" - api "com.android.support:appcompat-v7:${kau.supportLibs}" - api "com.android.support:support-v13:${kau.supportLibs}" - api "com.android.support:design:${kau.supportLibs}" - api "com.android.support:recyclerview-v7:${kau.supportLibs}" - api "com.android.support:cardview-v7:${kau.supportLibs}" - api "com.android.support.constraint:constraint-layout:${kau.constraintLayout}" + api "androidx.appcompat:appcompat:${kau.appcompat}" + api "androidx.recyclerview:recyclerview:${kau.recyclerView}" + api "androidx.cardview:cardview:${kau.cardView}" + api "androidx.constraintlayout:constraintlayout:${kau.constraintLayout}" + api "com.google.android.material:material:${kau.googleMaterial}" + + api "org.jetbrains.kotlinx:kotlinx-coroutines-android:${kau.coroutines}" api "com.mikepenz:iconics-core:${kau.iconics}@aar" api "com.mikepenz:google-material-typeface:${kau.iconicsGoogle}.original@aar" api "com.afollestad.material-dialogs:core:${kau.materialDialog}" - - api "org.jetbrains.anko:anko-commons:${kau.anko}" } apply from: '../artifacts.gradle' diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt index 2a9263a..29f5af1 100644 --- a/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.kt +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/kpref/KPrefTest.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.kpref import android.annotation.SuppressLint -import android.support.test.InstrumentationRegistry -import android.support.test.filters.MediumTest -import android.support.test.runner.AndroidJUnit4 +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -23,7 +39,7 @@ class KPrefTest { class TestPref : KPref() { init { - initialize(InstrumentationRegistry.getTargetContext(), "kpref_test_${System.currentTimeMillis()}") + initialize(ApplicationProvider.getApplicationContext<Context>(), "kpref_test_${System.currentTimeMillis()}") } var one by kpref("one", 1) @@ -83,11 +99,9 @@ class KPrefTest { assertEquals(-1, pref.one, "Kpref did not refetch from shared prefs upon reset") } - @Test fun single() { assertTrue(pref.oneShot) assertFalse(pref.oneShot) } - -}
\ No newline at end of file +} diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/utils/KotterknifeTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/utils/KotterknifeTest.kt index 1dac92f..96ebd3a 100644 --- a/core/src/androidTest/kotlin/ca/allanwang/kau/utils/KotterknifeTest.kt +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/utils/KotterknifeTest.kt @@ -1,12 +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.utils import android.content.Context -import android.support.test.InstrumentationRegistry -import android.support.test.filters.MediumTest -import android.support.test.runner.AndroidJUnit4 import android.view.View import android.widget.FrameLayout import android.widget.TextView +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import androidx.test.platform.app.InstrumentationRegistry import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -25,7 +40,7 @@ class KotterknifeTest { @Before fun init() { - context = InstrumentationRegistry.getContext() + context = InstrumentationRegistry.getInstrumentation().context } @Test @@ -197,4 +212,4 @@ class KotterknifeTest { view.id = id return view } -}
\ No newline at end of file +} diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/utils/UtilsAndroidTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/utils/UtilsAndroidTest.kt new file mode 100644 index 0000000..665e0b2 --- /dev/null +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/utils/UtilsAndroidTest.kt @@ -0,0 +1,50 @@ +/* + * Copyright 2018 Allan Wang + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package ca.allanwang.kau.utils + +import android.graphics.Color +import androidx.test.ext.junit.runners.AndroidJUnit4 +import org.junit.Test +import org.junit.runner.RunWith +import kotlin.test.assertEquals + +/** + * Created by Allan Wang on 2018-12-24. + * + * Misc test code that are dependent on android + */ +@RunWith(AndroidJUnit4::class) +class UtilsAndroidTest { + + @Test + fun colorBlend() { + assertEquals(0x22446688, 0x11335577.blendWith(0x33557799, 0.5f), "Failed to blend with 50% ratio") + assertEquals(0x11335577, 0x11335577.blendWith(0x33557799, 0.0f), "Failed to blend with 0% ratio") + assertEquals(0x33557799, 0x22446688.blendWith(0x33557799, 1.0f), "Failed to blend with 100% ratio") + } + + @Test + fun lighten() { + assertEquals(Color.WHITE, Color.WHITE.lighten(0.35f), "Should not be able to further lighten white") + assertEquals(0xFFEEAAEA.toInt(), 0xFFDD55D5.toInt().lighten(0.5f), "Failed to lighten color by 50%") + } + + @Test + fun darken() { + assertEquals(Color.BLACK, Color.BLACK.darken(0.35f), "Should not be able to further darken black") + assertEquals(0xFF224424.toInt(), 0xFF448848.toInt().darken(0.5f), "Failed to darken color by 50%") + } +} diff --git a/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt b/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt index f9ce24b..7ec2a41 100644 --- a/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt +++ b/core/src/androidTest/kotlin/ca/allanwang/kau/xml/FaqTest.kt @@ -1,8 +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.xml -import android.support.test.InstrumentationRegistry -import android.support.test.filters.MediumTest -import android.support.test.runner.AndroidJUnit4 +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import ca.allanwang.kau.test.R import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertEquals @@ -14,26 +31,16 @@ import kotlin.test.assertEquals @MediumTest class FaqTest { - @Test - fun simpleTest() { - InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq) { data -> - assertEquals(2, data.size, "FAQ size is incorrect") - assertEquals("1. This is a question", data.first().question.toString(), "First question does not match") - assertEquals("This is an answer", data.first().answer.toString(), "First answer does not match") - assertEquals("2. This is another question", data.last().question.toString(), "Second question does not match") - assertEquals("This is another answer", data.last().answer.toString(), "Second answer does not match") - } - } + val context: Context + get() = ApplicationProvider.getApplicationContext<Context>() @Test - fun withoutNumbering() { - InstrumentationRegistry.getTargetContext().kauParseFaq(R.xml.test_faq, false) { data -> - assertEquals(2, data.size, "FAQ size is incorrect") - assertEquals("This is a question", data.first().question.toString(), "First question does not match") - assertEquals("This is an answer", data.first().answer.toString(), "First answer does not match") - assertEquals("This is another question", data.last().question.toString(), "Second question does not match") - assertEquals("This is another answer", data.last().answer.toString(), "Second answer does not match") - } + fun simpleTest() { + val data = context.kauParseFaq(R.xml.test_faq, false) + assertEquals(2, data.size, "FAQ size is incorrect") + assertEquals("This is a question", data.first().question.toString(), "First question does not match") + assertEquals("This is an answer", data.first().answer.toString(), "First answer does not match") + assertEquals("This is another question", data.last().question.toString(), "Second question does not match") + assertEquals("This is another answer", data.last().answer.toString(), "Second answer does not match") } - -}
\ No newline at end of file +} 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) diff --git a/core/src/main/res-public/values-da-rDK/strings_commons.xml b/core/src/main/res-public/values-da-rDK/strings_commons.xml index 5be1277..745418a 100644 --- a/core/src/main/res-public/values-da-rDK/strings_commons.xml +++ b/core/src/main/res-public/values-da-rDK/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-de-rDE/strings_commons.xml b/core/src/main/res-public/values-de-rDE/strings_commons.xml index 015b612..e67292c 100644 --- a/core/src/main/res-public/values-de-rDE/strings_commons.xml +++ b/core/src/main/res-public/values-de-rDE/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-es-rES/strings_commons.xml b/core/src/main/res-public/values-es-rES/strings_commons.xml index 7cab287..34e4aa7 100644 --- a/core/src/main/res-public/values-es-rES/strings_commons.xml +++ b/core/src/main/res-public/values-es-rES/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-fr-rFR/strings_commons.xml b/core/src/main/res-public/values-fr-rFR/strings_commons.xml index 2d681ff..89a9a99 100644 --- a/core/src/main/res-public/values-fr-rFR/strings_commons.xml +++ b/core/src/main/res-public/values-fr-rFR/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-gl-rES/strings_commons.xml b/core/src/main/res-public/values-gl-rES/strings_commons.xml index b9a5564..8b4d1b4 100644 --- a/core/src/main/res-public/values-gl-rES/strings_commons.xml +++ b/core/src/main/res-public/values-gl-rES/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-hu-rHU/strings_commons.xml b/core/src/main/res-public/values-hu-rHU/strings_commons.xml index 5488f1d..bae6c1e 100644 --- a/core/src/main/res-public/values-hu-rHU/strings_commons.xml +++ b/core/src/main/res-public/values-hu-rHU/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-in-rID/strings_commons.xml b/core/src/main/res-public/values-in-rID/strings_commons.xml index b734377..5532b67 100644 --- a/core/src/main/res-public/values-in-rID/strings_commons.xml +++ b/core/src/main/res-public/values-in-rID/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-it-rIT/strings_commons.xml b/core/src/main/res-public/values-it-rIT/strings_commons.xml index 821191e..c2d513a 100644 --- a/core/src/main/res-public/values-it-rIT/strings_commons.xml +++ b/core/src/main/res-public/values-it-rIT/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-ko-rKR/strings_commons.xml b/core/src/main/res-public/values-ko-rKR/strings_commons.xml index 112e8d5..48153cb 100644 --- a/core/src/main/res-public/values-ko-rKR/strings_commons.xml +++ b/core/src/main/res-public/values-ko-rKR/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-nl-rNL/strings_commons.xml b/core/src/main/res-public/values-nl-rNL/strings_commons.xml new file mode 100644 index 0000000..a3967ff --- /dev/null +++ b/core/src/main/res-public/values-nl-rNL/strings_commons.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- +A collection of common string values +Most resources are verbatim and x represents a formatted item +--> +<resources> + <string name="kau_about_app">Over de app</string> + <string name="kau_about_x">Over %s</string> + <string name="kau_add_account">Account toevoegen</string> + <string name="kau_back">Terug</string> + <string name="kau_cancel">Annuleren</string> + <string name="kau_changelog">Veranderingen</string> + <string name="kau_close">Sluiten</string> + <string name="kau_contact_us">Contact opnemen</string> + <string name="kau_copy">Kopiëren</string> + <string name="kau_custom">Aangepast</string> + <string name="kau_dark">Donker</string> + <string name="kau_default">Standaard</string> + <string name="kau_do_not_show_again">Niet opnieuw tonen</string> + <string name="kau_done">Gereed</string> + <string name="kau_error">Fout</string> + <string name="kau_exit">Afsluiten</string> + <string name="kau_exit_confirmation">Afsluiten?</string> + <string name="kau_exit_confirmation_x">%s afsluiten?</string> + <string name="kau_glass">Glas</string> + <string name="kau_got_it">Begrepen</string> + <string name="kau_great">Geweldig</string> + <string name="kau_hide">Verbergen</string> + <string name="kau_light">Licht</string> + <string name="kau_login">Aanmelden</string> + <string name="kau_logout">Afmelden</string> + <string name="kau_logout_confirm_as_x">%s uitloggen?</string> + <string name="kau_manage_account">Account beheren</string> + <string name="kau_maybe">Misschien</string> + <string name="kau_menu">Menu</string> + <string name="kau_no">Nee</string> + <string name="kau_no_results_found">Geen resultaten</string> + <string name="kau_none">Geen</string> + <string name="kau_ok">OK</string> + <string name="kau_play_store">Play Store</string> + <string name="kau_rate">Beoordelen</string> + <string name="kau_report_bug">Een fout melden</string> + <string name="kau_search">Zoeken</string> + <string name="kau_send_feedback">Feedback geven</string> + <string name="kau_send_via">Verzenden via</string> + <string name="kau_settings">Instellingen</string> + <string name="kau_share">Delen</string> + <string name="kau_text_copied">Tekst naar klembord gekopieerd.</string> + <string name="kau_thank_you">Bedankt</string> + <string name="kau_uh_oh">Oeps</string> + <string name="kau_warning">Waarschuwing</string> + <plurals name="kau_x_days"> + <item quantity="one">%d dag</item> + <item quantity="other">%d dagen</item> + </plurals> + <plurals name="kau_x_hours"> + <item quantity="one">%d uur</item> + <item quantity="other">%d uren</item> + </plurals> + <plurals name="kau_x_minutes"> + <item quantity="one">%d minuut</item> + <item quantity="other">%d minuten</item> + </plurals> + <plurals name="kau_x_seconds"> + <item quantity="one">%d seconde</item> + <item quantity="other">%d seconden</item> + </plurals> + <string name="kau_yes">Ja</string> + <string name="kau_permission_denied">Toestemming geweigerd</string> +</resources> diff --git a/core/src/main/res-public/values-no-rNO/strings_commons.xml b/core/src/main/res-public/values-no-rNO/strings_commons.xml index c18b439..e27f20d 100644 --- a/core/src/main/res-public/values-no-rNO/strings_commons.xml +++ b/core/src/main/res-public/values-no-rNO/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-pl-rPL/strings_commons.xml b/core/src/main/res-public/values-pl-rPL/strings_commons.xml index 248e409..323a0ff 100644 --- a/core/src/main/res-public/values-pl-rPL/strings_commons.xml +++ b/core/src/main/res-public/values-pl-rPL/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-pt-rBR/strings_commons.xml b/core/src/main/res-public/values-pt-rBR/strings_commons.xml index 716e373..4eaea6c 100644 --- a/core/src/main/res-public/values-pt-rBR/strings_commons.xml +++ b/core/src/main/res-public/values-pt-rBR/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-pt-rPT/strings_commons.xml b/core/src/main/res-public/values-pt-rPT/strings_commons.xml new file mode 100644 index 0000000..863b8b7 --- /dev/null +++ b/core/src/main/res-public/values-pt-rPT/strings_commons.xml @@ -0,0 +1,71 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- +A collection of common string values +Most resources are verbatim and x represents a formatted item +--> +<resources> + <string name="kau_about_app">Acerca</string> + <string name="kau_about_x">Acerca de %s</string> + <string name="kau_add_account">Adicionar conta</string> + <string name="kau_back">Voltar</string> + <string name="kau_cancel">Cancelar</string> + <string name="kau_changelog">Registo de alterações</string> + <string name="kau_close">Fechar</string> + <string name="kau_contact_us">Contacte-nos</string> + <string name="kau_copy">Copiar</string> + <string name="kau_custom">Personalizada</string> + <string name="kau_dark">Escuro</string> + <string name="kau_default">Padrão</string> + <string name="kau_do_not_show_again">Não mostrar novamente</string> + <string name="kau_done">Feito</string> + <string name="kau_error">Erro</string> + <string name="kau_exit">Sair</string> + <string name="kau_exit_confirmation">Tem a certeza de que deseja sair?</string> + <string name="kau_exit_confirmation_x">Tem a certeza de que deseja sair de %s?</string> + <string name="kau_glass">Vidro</string> + <string name="kau_got_it">Percebi</string> + <string name="kau_great">Excelente</string> + <string name="kau_hide">Ocultar</string> + <string name="kau_light">Claro</string> + <string name="kau_login">Iniciar sessão</string> + <string name="kau_logout">Sair</string> + <string name="kau_logout_confirm_as_x">Tem a certeza de que deseja sair como %s?</string> + <string name="kau_manage_account">Gerir conta</string> + <string name="kau_maybe">Talvez</string> + <string name="kau_menu">Menu</string> + <string name="kau_no">Não</string> + <string name="kau_no_results_found">Não existem resultados</string> + <string name="kau_none">Nenhum</string> + <string name="kau_ok">OK</string> + <string name="kau_play_store">Play Store</string> + <string name="kau_rate">Avaliar</string> + <string name="kau_report_bug">Comunicar um erro</string> + <string name="kau_search">Procurar</string> + <string name="kau_send_feedback">Enviar comentários</string> + <string name="kau_send_via">Enviar via</string> + <string name="kau_settings">Definições</string> + <string name="kau_share">Partilhar</string> + <string name="kau_text_copied">Texto copiado para a área de transferência.</string> + <string name="kau_thank_you">Obrigado</string> + <string name="kau_uh_oh">Bolas</string> + <string name="kau_warning">Aviso</string> + <plurals name="kau_x_days"> + <item quantity="one">%d dia</item> + <item quantity="other">%d dias</item> + </plurals> + <plurals name="kau_x_hours"> + <item quantity="one">%d hora</item> + <item quantity="other">%d horas</item> + </plurals> + <plurals name="kau_x_minutes"> + <item quantity="one">%d minuto</item> + <item quantity="other">%d minutos</item> + </plurals> + <plurals name="kau_x_seconds"> + <item quantity="one">%d segundo</item> + <item quantity="other">%d segundos</item> + </plurals> + <string name="kau_yes">Sim</string> + <string name="kau_permission_denied">Permissão negada</string> +</resources> diff --git a/core/src/main/res-public/values-sr-rSP/strings_commons.xml b/core/src/main/res-public/values-sr-rSP/strings_commons.xml new file mode 100644 index 0000000..213d876 --- /dev/null +++ b/core/src/main/res-public/values-sr-rSP/strings_commons.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- +A collection of common string values +Most resources are verbatim and x represents a formatted item +--> +<resources> + <string name="kau_about_app">О апликацији</string> + <string name="kau_about_x">О %s</string> + <string name="kau_add_account">Додајте налог</string> + <string name="kau_back">Назад</string> + <string name="kau_cancel">Откажи</string> + <string name="kau_changelog">Евиденција измена</string> + <string name="kau_close">Затвори</string> + <string name="kau_contact_us">Контактирајте нас</string> + <string name="kau_copy">Копирај</string> + <string name="kau_custom">Прилагођено</string> + <string name="kau_dark">Тамно</string> + <string name="kau_default">Подразумевано</string> + <string name="kau_do_not_show_again">Не приказуј поново</string> + <string name="kau_done">Готово</string> + <string name="kau_error">Грешка</string> + <string name="kau_exit">Изађи</string> + <string name="kau_exit_confirmation">Да ли сте сигурни да желите да изађете?</string> + <string name="kau_exit_confirmation_x">Да ли сте сигурно желите да напустите %s?</string> + <string name="kau_glass">Стакло</string> + <string name="kau_got_it">Разумем</string> + <string name="kau_great">Одлично</string> + <string name="kau_hide">Сакриј</string> + <string name="kau_light">Светло</string> + <string name="kau_login">Пријава</string> + <string name="kau_logout">Одјави се</string> + <string name="kau_logout_confirm_as_x">Заиста желите да се одјавите са %s?</string> + <string name="kau_manage_account">Управљај налозима</string> + <string name="kau_maybe">Можда</string> + <string name="kau_menu">Мени</string> + <string name="kau_no">Не</string> + <string name="kau_no_results_found">Нема резултата</string> + <string name="kau_none">Ниједно</string> + <string name="kau_ok">ОК</string> + <string name="kau_play_store">Плеј продавница</string> + <string name="kau_rate">Оцени</string> + <string name="kau_report_bug">Пријави грешку</string> + <string name="kau_search">Претражи</string> + <string name="kau_send_feedback">Пошаљите повратне информације</string> + <string name="kau_send_via">Пошаљи преко</string> + <string name="kau_settings">Подешавања</string> + <string name="kau_share">Подели</string> + <string name="kau_text_copied">Текст је копиран у привремену меморију.</string> + <string name="kau_thank_you">Хвала</string> + <string name="kau_uh_oh">Јао не</string> + <string name="kau_warning">Упозорење</string> + <plurals name="kau_x_days"> + <item quantity="one">%d дан</item> + <item quantity="few">%d дана</item> + <item quantity="other">%d дана</item> + </plurals> + <plurals name="kau_x_hours"> + <item quantity="one">%d сат</item> + <item quantity="few">%d сати</item> + <item quantity="other">%d сати</item> + </plurals> + <plurals name="kau_x_minutes"> + <item quantity="one">%d минут</item> + <item quantity="few">%d минута</item> + <item quantity="other">%d минута</item> + </plurals> + <plurals name="kau_x_seconds"> + <item quantity="one">%d секунду</item> + <item quantity="few">%d сек</item> + <item quantity="other">%d секунди</item> + </plurals> + <string name="kau_yes">Да</string> + <string name="kau_permission_denied">Немате овлашћења</string> +</resources> diff --git a/core/src/main/res-public/values-sv-rSE/strings_commons.xml b/core/src/main/res-public/values-sv-rSE/strings_commons.xml index 6cc2bb3..8d4362b 100644 --- a/core/src/main/res-public/values-sv-rSE/strings_commons.xml +++ b/core/src/main/res-public/values-sv-rSE/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-th-rTH/strings_commons.xml b/core/src/main/res-public/values-th-rTH/strings_commons.xml index 2f4bdec..e47273d 100644 --- a/core/src/main/res-public/values-th-rTH/strings_commons.xml +++ b/core/src/main/res-public/values-th-rTH/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-tr-rTR/strings_commons.xml b/core/src/main/res-public/values-tr-rTR/strings_commons.xml index 1e8395e..b36ca43 100644 --- a/core/src/main/res-public/values-tr-rTR/strings_commons.xml +++ b/core/src/main/res-public/values-tr-rTR/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-uk-rUA/strings_commons.xml b/core/src/main/res-public/values-uk-rUA/strings_commons.xml index 60ca520..0f62c6a 100644 --- a/core/src/main/res-public/values-uk-rUA/strings_commons.xml +++ b/core/src/main/res-public/values-uk-rUA/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> @@ -48,6 +50,30 @@ Most resources are verbatim and x represents a formatted item <string name="kau_thank_you">Дякуємо Вам</string> <string name="kau_uh_oh">Ой-ой</string> <string name="kau_warning">Увага</string> + <plurals name="kau_x_days"> + <item quantity="one">%d день</item> + <item quantity="few">%d днів</item> + <item quantity="many">%d днів</item> + <item quantity="other">%d днів</item> + </plurals> + <plurals name="kau_x_hours"> + <item quantity="one">%d годину</item> + <item quantity="few">%d годин</item> + <item quantity="many">%d годин</item> + <item quantity="other">%d годин</item> + </plurals> + <plurals name="kau_x_minutes"> + <item quantity="one">%d хвилини</item> + <item quantity="few">%d хвилин</item> + <item quantity="many">%d хвилин</item> + <item quantity="other">%d хвилин</item> + </plurals> + <plurals name="kau_x_seconds"> + <item quantity="one">%d секунди</item> + <item quantity="few">%d секунд</item> + <item quantity="many">%d секунд</item> + <item quantity="other">%d секунд</item> + </plurals> <string name="kau_yes">Так</string> <string name="kau_permission_denied">У доступі відмовлено</string> </resources> diff --git a/core/src/main/res-public/values-vi-rVN/strings_commons.xml b/core/src/main/res-public/values-vi-rVN/strings_commons.xml index 767e628..a444dc2 100644 --- a/core/src/main/res-public/values-vi-rVN/strings_commons.xml +++ b/core/src/main/res-public/values-vi-rVN/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-zh-rCN/strings_commons.xml b/core/src/main/res-public/values-zh-rCN/strings_commons.xml index 47ddbda..66a7423 100644 --- a/core/src/main/res-public/values-zh-rCN/strings_commons.xml +++ b/core/src/main/res-public/values-zh-rCN/strings_commons.xml @@ -1,4 +1,6 @@ -<?xml version="1.0" encoding="utf-8" standalone="no"?><!--Generated by crowdin.com--><!-- +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- A collection of common string values Most resources are verbatim and x represents a formatted item --> diff --git a/core/src/main/res-public/values-zh-rTW/strings_commons.xml b/core/src/main/res-public/values-zh-rTW/strings_commons.xml new file mode 100644 index 0000000..bf9f59b --- /dev/null +++ b/core/src/main/res-public/values-zh-rTW/strings_commons.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<!--Generated by crowdin.com--> +<!-- +A collection of common string values +Most resources are verbatim and x represents a formatted item +--> +<resources> + <string name="kau_about_app">關於此程式</string> + <string name="kau_about_x">關於 %s</string> + <string name="kau_add_account">新增帳號</string> + <string name="kau_back">回上頁</string> + <string name="kau_cancel">取消</string> + <string name="kau_changelog">更新資訊</string> + <string name="kau_close">關閉</string> + <string name="kau_contact_us">與我們聯繫</string> + <string name="kau_copy">複製</string> + <string name="kau_custom">自訂</string> + <string name="kau_dark">黑色主題</string> + <string name="kau_default">默認</string> + <string name="kau_do_not_show_again">不要再顯示</string> + <string name="kau_done">完成</string> + <string name="kau_error">錯誤</string> + <string name="kau_exit">結束</string> + <string name="kau_exit_confirmation">您確定要離開嗎?</string> + <string name="kau_exit_confirmation_x">您確定要離開%s嗎?</string> + <string name="kau_glass">玻璃</string> + <string name="kau_got_it">知道了</string> + <string name="kau_great">讚</string> + <string name="kau_hide">隱藏</string> + <string name="kau_light">亮色調</string> + <string name="kau_login">登入</string> + <string name="kau_logout">登出</string> + <string name="kau_logout_confirm_as_x">您確定要登出%s嗎?</string> + <string name="kau_manage_account">帳戶管理</string> + <string name="kau_maybe">可能</string> + <string name="kau_menu">選單</string> + <string name="kau_no">不</string> + <string name="kau_no_results_found">找不到相符的結果</string> + <string name="kau_none">無</string> + <string name="kau_ok">確定</string> + <string name="kau_play_store">Play商店</string> + <string name="kau_rate">評分</string> + <string name="kau_report_bug">回報程式錯誤</string> + <string name="kau_search">搜尋</string> + <string name="kau_send_feedback">回饋意見</string> + <string name="kau_send_via">來發送</string> + <string name="kau_settings">設定</string> + <string name="kau_share">分享</string> + <string name="kau_text_copied">文字已複製到剪貼簿</string> + <string name="kau_thank_you">感謝</string> + <string name="kau_uh_oh">噢!</string> + <string name="kau_warning">警告</string> + <plurals name="kau_x_days"> + <item quantity="other">%d 天</item> + </plurals> + <plurals name="kau_x_hours"> + <item quantity="other">%d 小時</item> + </plurals> + <plurals name="kau_x_minutes"> + <item quantity="other">%d 分鐘</item> + </plurals> + <plurals name="kau_x_seconds"> + <item quantity="other">%d 秒</item> + </plurals> + <string name="kau_yes">是</string> + <string name="kau_permission_denied">權限不足</string> +</resources> diff --git a/core/src/main/res/layout/kau_changelog_content.xml b/core/src/main/res/layout/kau_changelog_content.xml index 5c463aa..ab94956 100644 --- a/core/src/main/res/layout/kau_changelog_content.xml +++ b/core/src/main/res/layout/kau_changelog_content.xml @@ -3,9 +3,9 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="horizontal" - android:paddingBottom="8.4sp" android:paddingLeft="@dimen/kau_dialog_margin" - android:paddingRight="@dimen/kau_dialog_margin"> + android:paddingRight="@dimen/kau_dialog_margin" + android:paddingBottom="8.4sp"> <!--padding bottom is 14sp * 0.6--> diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/CoroutineTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/CoroutineTest.kt new file mode 100644 index 0000000..1e86305 --- /dev/null +++ b/core/src/test/kotlin/ca/allanwang/kau/kotlin/CoroutineTest.kt @@ -0,0 +1,73 @@ +/* + * 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.kotlin + +import kotlinx.coroutines.CancellationException +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext +import org.junit.Test +import kotlin.test.assertEquals +import kotlin.test.fail + +/** + * Tests geared towards coroutines + */ +class CoroutineTest { + + /** + * If a job is cancelled, then a switch to a new context will not run + */ + @Test + fun implicitCancellationBefore() { + val job = Job() + var id = 0 + try { + runBlocking(job) { + id++ + job.cancel() + withContext(Dispatchers.IO) { + fail("Context switch should not be reached") + } + } + } catch (ignore: CancellationException) { + } finally { + assertEquals(1, id, "Launcher never executed") + } + } + + /** + * If a job is cancelled, then a switch from a new context will not run + */ + @Test + fun implicitCancellationAfter() { + val job = Job() + var id = 0 + try { + runBlocking(job) { + withContext(Dispatchers.IO) { + id++ + job.cancel() + } + fail("Post context switch should not be reached") + } + } catch (ignore: CancellationException) { + } finally { + assertEquals(1, id, "Context switch never executed") + } + } +} diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/DebounceTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/DebounceTest.kt index 8ccdab3..c406901 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/kotlin/DebounceTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/kotlin/DebounceTest.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 org.junit.Test @@ -48,5 +63,4 @@ class DebounceTest { Thread.sleep(30) assertEquals(10, i) } - -}
\ No newline at end of file +} diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/LazyResettableTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/LazyResettableTest.kt index 2025422..eaaaacb 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/kotlin/LazyResettableTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/kotlin/LazyResettableTest.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 org.junit.Before @@ -33,5 +48,4 @@ class LazyResettableTest { assertEquals(t1, t2, "Lazy resettable not returning same value after second call") assertNotEquals(t1, t3, "Lazy resettable not invalidated by registry") } - -}
\ No newline at end of file +} diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/StreamsTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/StreamsTest.kt index 1c40f57..4dc4a34 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/kotlin/StreamsTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/kotlin/StreamsTest.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 org.junit.Test @@ -38,5 +53,4 @@ class StreamsTest { items.kauRemoveIf { it == thePotato } //removal by equality assertEquals(result.size - 1, items.size, "Invalid list removal based on equality") } - -}
\ No newline at end of file +} diff --git a/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt b/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt deleted file mode 100644 index 7eeffaf..0000000 --- a/core/src/test/kotlin/ca/allanwang/kau/kotlin/ZipTest.kt +++ /dev/null @@ -1,70 +0,0 @@ -package ca.allanwang.kau.kotlin - -import org.jetbrains.anko.doAsync -import org.junit.Test -import java.util.* -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import kotlin.test.assertTrue - -/** - * Created by Allan Wang on 2017-08-06. - */ -class ZipTest { - - val debug = false - - fun p(text: String) { - if (debug) println(text) - } - - @Test - fun basic() { - val start = System.currentTimeMillis() - val latch = CountDownLatch(1) - val rnd = Random() - (0..10).map { - { callback: ZipCallback<Int> -> - doAsync { - val sleepTime = rnd.nextInt(100) + 200L - p("Task $it will sleep for ${sleepTime}ms") - Thread.sleep(sleepTime) - val finish = System.currentTimeMillis() - p("Task $it finished in ${finish - start}ms at $finish") - callback(it) - }; Unit - } - }.zip(-1) { results -> - val finish = System.currentTimeMillis() - println("Results ${results.contentToString()} received in ${finish - start}ms at $finish") - assertTrue((0..10).toList().toTypedArray().contentEquals(results), "Basic zip results do not match") - assertTrue(finish - start < 1000L, "Basic zip does not seem to be running asynchronously") - latch.countDown() - - } - latch.await(1100, TimeUnit.MILLISECONDS) - } - - @Test - fun basicAsync() { - val start = System.currentTimeMillis() - val latch = CountDownLatch(1) - val rnd = Random() - (0..10).map { - { - val sleepTime = rnd.nextInt(100) + 200L - p("Task $it will sleep for ${sleepTime}ms") - Thread.sleep(sleepTime) - val finish = System.currentTimeMillis() - p("Task $it finished in ${finish - start}ms at $finish") - } - }.zipAsync { - val finish = System.currentTimeMillis() - println("Results received in ${finish - start}ms at $finish") - assertTrue(finish - start < 1000L, "BasicAsync does not seem to be wrapping the tasks asynchronously") - latch.countDown() - } - latch.await(1100, TimeUnit.MILLISECONDS) - } - -}
\ No newline at end of file diff --git a/core/src/test/kotlin/ca/allanwang/kau/utils/UtilsTest.kt b/core/src/test/kotlin/ca/allanwang/kau/utils/UtilsTest.kt index ce2b757..b9c200a 100644 --- a/core/src/test/kotlin/ca/allanwang/kau/utils/UtilsTest.kt +++ b/core/src/test/kotlin/ca/allanwang/kau/utils/UtilsTest.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.graphics.Color @@ -17,9 +32,27 @@ class UtilsTest { } @Test + fun colorWithAlpha() { + val origColor = 0xFF123456.toInt() + assertEquals(0x00123456, origColor.withAlpha(0), "Failed to convert with alpha 0") + assertEquals(0x50123456, origColor.withAlpha(80), "Failed to convert with alpha 80") + assertEquals(0xFF123456.toInt(), origColor.withAlpha(255), "Failed to convert with alpha 255") + assertEquals(0xFF123456.toInt(), origColor.withAlpha(0xFF), "Failed to convert with alpha 0xFF") + assertEquals(Color.TRANSPARENT, Color.BLACK.withAlpha(0), "Failed to convert black to transparent") + } + + @Test + fun colorWithMinAlpha() { + val origColor = 0x80123456.toInt() + assertEquals(origColor, origColor.withMinAlpha(0), "Failed to convert with min alpha 0") + assertEquals(0xFA123456.toInt(), origColor.withMinAlpha(0xFA), "Failed to convert with min alpha 0xFA") + assertEquals(Color.BLUE, Color.BLUE.withMinAlpha(89), "Failed to convert blue with min alpha 89") + } + + @Test fun rounding() { assertEquals("1.23", 1.23456f.round(2)) assertEquals("22.466", 22.465920439.round(3)) assertEquals("22", 22f.round(3)) } -}
\ No newline at end of file +} |