From 0fd6ad98a4ab713264a1969610bc06ed455b86cd Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 31 Jul 2022 20:22:35 +0800 Subject: [PATCH 1/4] Add extensions in base module --- .../demoapp/base/util/BaseBindingAdapters.kt | 19 +++++++ .../demoapp/base/util/BaseExtensions.kt | 54 +++++++++++++++++-- .../demoapp/base/widget/StatusBarView.kt | 4 +- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseBindingAdapters.kt b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseBindingAdapters.kt index 6c04bce70..6ef34bdb3 100644 --- a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseBindingAdapters.kt +++ b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseBindingAdapters.kt @@ -5,17 +5,23 @@ package io.goooler.demoapp.base.util import android.graphics.Color import android.graphics.Outline import android.graphics.Paint +import android.graphics.PorterDuff import android.graphics.Typeface import android.graphics.drawable.GradientDrawable import android.view.View import android.view.ViewGroup import android.view.ViewOutlineProvider +import android.widget.ImageView import android.widget.TextView import androidx.annotation.ColorInt import androidx.annotation.Px import androidx.databinding.BindingAdapter // ------------------------View --------------------------// +@BindingAdapter("binding_isEnabled") +internal fun View.bindingIsEnabled(enabled: Boolean) { + this.isEnabled = enabled +} @BindingAdapter("binding_isGone") internal fun View.bindingIsGone(gone: Boolean) { @@ -81,6 +87,19 @@ internal fun View.bindingMarginEnd(@Px margin: Float) { marginDirection(2, margin) } +@BindingAdapter("binding_onLongClick") +internal fun View.bindingOnLongClick(body: () -> Unit) { + setOnLongClickListener { + body() + true + } +} + +@BindingAdapter("binding_tint") +fun ImageView.bindingTint(@ColorInt color: Int) { + setColorFilter(color, PorterDuff.Mode.SRC_IN) +} + // ------------------------View Bg Shape---------------------// @BindingAdapter( diff --git a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt index 6228195ed..c22f66e6f 100644 --- a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt +++ b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt @@ -102,8 +102,50 @@ fun T.deepCopy(): T? { } } +@Throws(ReflectiveOperationException::class) +fun lazyReflectedMethod( + declaringClass: Class<*>, + methodName: String, + vararg parameterTypes: Any +): Lazy = lazy { + getReflectedMethod(declaringClass, methodName, *getParameterTypes(parameterTypes)) +} + +@Throws(ReflectiveOperationException::class) +fun getParameterTypes(parameterTypes: Array): Array> = + Array(parameterTypes.size) { + when (val parameterType = parameterTypes[it]) { + is Class<*> -> parameterType + is String -> Class.forName(parameterType) + else -> throw IllegalArgumentException(parameterType.toString()) + } + } + +@Throws(ReflectiveOperationException::class) +fun getReflectedMethod( + declaringClass: Class<*>, + methodName: String, + vararg parameterTypes: Class<*> +): Method = + declaringClass.getDeclaredMethod(methodName, *parameterTypes).also { it.isAccessible = true } + // ---------------------CharSequence-------------------------------// +/** + * Validate given text is a valid filename. + * + * @return true if given text is a valid filename + */ +fun String.isValidFilename(): Boolean { + val filenameRegex = + Pattern.compile("[\\\\/:*?\"<>|\\x01-\\x1F\\x7F]", Pattern.CASE_INSENSITIVE) + + // It's not easy to use regex to detect single/double dot while leaving valid values + // (filename.zip) behind... + // So we simply use equality to check them + return !filenameRegex.matcher(this).find() && "." != this && ".." != this +} + operator fun String.times(@IntRange(from = 0) num: Int): String { require(num >= 0) { "Param num should >= 0" @@ -238,6 +280,8 @@ fun String?.isNetworkUrl(): Boolean = URLUtil.isNetworkUrl(this) fun String?.isValidUrl(): Boolean = URLUtil.isValidUrl(this) +fun Uri.withAppendedId(id: Long): Uri = ContentUris.withAppendedId(this, id) + // ---------------------Calculate-------------------------------// infix fun Double.plus(that: Double): Double { @@ -391,17 +435,17 @@ fun Intent.getStringExtra(name: String, defaultValue: String): String = fun Intent.getCharSequenceExtra(name: String, defaultValue: CharSequence): CharSequence = getCharSequenceExtra(name) ?: defaultValue -fun Intent.getParcelableExtra(name: String, defaultValue: T): T = +inline fun Intent.getParcelableExtra(name: String, defaultValue: T): T = getParcelableExtra(name) ?: defaultValue -fun Intent.getSerializableExtra( +inline fun Intent.getSerializableExtra( name: String, - defaultValue: Serializable -): Serializable = getSerializableExtra(name) ?: defaultValue + defaultValue: T +): T = (getSerializableExtra(name) ?: defaultValue) as T // ---------------------Fragment-------------------------------// -fun T.putArguments(bundle: Bundle): T { +fun T.putArguments(bundle: Bundle?): T { arguments = bundle return this } diff --git a/base/src/main/kotlin/io/goooler/demoapp/base/widget/StatusBarView.kt b/base/src/main/kotlin/io/goooler/demoapp/base/widget/StatusBarView.kt index 8e61b9aac..18d275287 100644 --- a/base/src/main/kotlin/io/goooler/demoapp/base/widget/StatusBarView.kt +++ b/base/src/main/kotlin/io/goooler/demoapp/base/widget/StatusBarView.kt @@ -18,7 +18,7 @@ class StatusBarView(context: Context, attrs: AttributeSet? = null) : View(contex private fun getStatusBarHeight(): Int { val resources = Resources.getSystem() @SuppressLint("InternalInsetResource") - @DimenRes val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android") - return resources.getDimensionPixelSize(resourceId) + @DimenRes val resId = resources.getIdentifier("status_bar_height", "dimen", "android") + return resources.getDimensionPixelSize(resId) } } From 6ffb7d28b2bb9fcd5f8d9f35f1c60a889f32547b Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 31 Jul 2022 20:30:28 +0800 Subject: [PATCH 2/4] Add extensions in common module --- .../base/binding/BaseBindingActivity.kt | 16 +++++- .../base/binding/BaseBindingDialogFragment.kt | 2 +- .../base/binding/BaseBindingFragment.kt | 2 +- .../demoapp/common/util/CommonExtensions.kt | 55 ++++++++++--------- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingActivity.kt b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingActivity.kt index 1be3237ab..9f61e8a5b 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingActivity.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingActivity.kt @@ -4,13 +4,15 @@ import android.annotation.SuppressLint import android.content.pm.ActivityInfo import android.content.res.Resources import android.os.Bundle +import android.view.LayoutInflater import android.view.WindowManager import androidx.databinding.ViewDataBinding +import androidx.viewbinding.ViewBinding import com.blankj.utilcode.util.AdaptScreenUtils import com.blankj.utilcode.util.BarUtils import com.blankj.utilcode.util.ScreenUtils import io.goooler.demoapp.base.core.BaseActivity -import io.goooler.demoapp.common.util.inflateBinding +import java.lang.reflect.ParameterizedType abstract class BaseBindingActivity : BaseActivity(), IBinding { @@ -39,4 +41,16 @@ abstract class BaseBindingActivity : BaseActivity(), IBind else AdaptScreenUtils.adaptHeight(super.getResources(), 640) } + + companion object { + @Suppress("UNCHECKED_CAST") + internal fun Any.inflateBinding(inflater: LayoutInflater): T { + return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments + .filterIsInstance>() + .first() + .getDeclaredMethod("inflate", LayoutInflater::class.java) + .also { it.isAccessible = true } + .invoke(null, inflater) as T + } + } } diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingDialogFragment.kt b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingDialogFragment.kt index 78311a368..ef5e9a744 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingDialogFragment.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingDialogFragment.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.databinding.ViewDataBinding import io.goooler.demoapp.base.core.BaseDialogFragment -import io.goooler.demoapp.common.util.inflateBinding +import io.goooler.demoapp.common.base.binding.BaseBindingActivity.Companion.inflateBinding abstract class BaseBindingDialogFragment : BaseDialogFragment(), diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingFragment.kt b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingFragment.kt index f133dec9e..e7bf346c1 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingFragment.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/base/binding/BaseBindingFragment.kt @@ -6,7 +6,7 @@ import android.view.View import android.view.ViewGroup import androidx.databinding.ViewDataBinding import io.goooler.demoapp.base.core.BaseFragment -import io.goooler.demoapp.common.util.inflateBinding +import io.goooler.demoapp.common.base.binding.BaseBindingActivity.Companion.inflateBinding abstract class BaseBindingFragment : BaseFragment(), diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt index 337159586..b12f1aef0 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt @@ -3,9 +3,12 @@ package io.goooler.demoapp.common.util +import android.content.ContentResolver +import android.content.pm.PackageManager import android.graphics.Bitmap import android.graphics.drawable.Drawable -import android.view.LayoutInflater +import android.text.format.DateFormat +import android.text.format.Formatter import android.view.View import android.widget.TextView import androidx.annotation.AnyThread @@ -63,22 +66,22 @@ fun @receiver:StringRes Int.showToast() { ToastUtil.show(CommonApplication.app, this) } -@MainThread -fun SmartRefreshLayout.finishRefreshAndLoadMore() { - finishRefresh() - finishLoadMore() +inline fun DiffUtil.ItemCallback.asConfig(): AsyncDifferConfig { + return AsyncDifferConfig.Builder(this) + .setBackgroundThreadExecutor(Dispatchers.Default.asExecutor()) + .build() } -@MainThread -fun SmartRefreshLayout.enableRefreshAndLoadMore(enable: Boolean = true) { - setEnableRefresh(enable) - setEnableLoadMore(enable) -} +val contentResolver: ContentResolver get() = CommonApplication.app.contentResolver -@MainThread -fun SmartRefreshLayout.disableRefreshAndLoadMore() { - enableRefreshAndLoadMore(false) -} +val packageManager: PackageManager get() = CommonApplication.app.packageManager + +// ---------------------Unit-------------------------------// + +fun Long.formatFileSize(): String = Formatter.formatFileSize(CommonApplication.app, this) + +fun Long.millis2String(pattern: String = "yyyyMMddHHmmss"): String = + TimeUtils.millis2String(this, DateFormat.getBestDateTimePattern(Locale.getDefault(), pattern)) // ---------------------String-------------------------------// @@ -158,19 +161,19 @@ fun TextView.hideTextInputLayoutErrorOnTextChange(textInputLayout: TextInputLayo doAfterTextChanged { textInputLayout.error = null } } -inline fun DiffUtil.ItemCallback.asConfig(): AsyncDifferConfig { - return AsyncDifferConfig.Builder(this) - .setBackgroundThreadExecutor(Dispatchers.Default.asExecutor()) - .build() +@MainThread +fun SmartRefreshLayout.finishRefreshAndLoadMore() { + finishRefresh() + finishLoadMore() } -// ---------------------VM & Binding-------------------------------// +@MainThread +fun SmartRefreshLayout.enableRefreshAndLoadMore(enable: Boolean = true) { + setEnableRefresh(enable) + setEnableLoadMore(enable) +} -@Suppress("UNCHECKED_CAST") -fun LifecycleOwner.inflateBinding(inflater: LayoutInflater): T { - return (javaClass.genericSuperclass as ParameterizedType).actualTypeArguments - .filterIsInstance>() - .first() - .getDeclaredMethod("inflate", LayoutInflater::class.java) - .invoke(null, inflater) as T +@MainThread +fun SmartRefreshLayout.disableRefreshAndLoadMore() { + enableRefreshAndLoadMore(false) } From dbe54d7320ac6822cc5cf4a9b0ee3b1c2a8ca135 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 31 Jul 2022 20:31:52 +0800 Subject: [PATCH 3/4] Reformat --- .../demoapp/base/util/BaseExtensions.kt | 23 +++++-------------- .../demoapp/common/util/CommonExtensions.kt | 5 ++-- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt index c22f66e6f..7a86d7452 100644 --- a/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt +++ b/base/src/main/kotlin/io/goooler/demoapp/base/util/BaseExtensions.kt @@ -49,6 +49,7 @@ import androidx.lifecycle.coroutineScope import androidx.lifecycle.findViewTreeLifecycleOwner import java.io.File import java.io.Serializable +import java.lang.reflect.Method import java.math.BigDecimal import java.util.Collections import java.util.UUID @@ -131,21 +132,6 @@ fun getReflectedMethod( // ---------------------CharSequence-------------------------------// -/** - * Validate given text is a valid filename. - * - * @return true if given text is a valid filename - */ -fun String.isValidFilename(): Boolean { - val filenameRegex = - Pattern.compile("[\\\\/:*?\"<>|\\x01-\\x1F\\x7F]", Pattern.CASE_INSENSITIVE) - - // It's not easy to use regex to detect single/double dot while leaving valid values - // (filename.zip) behind... - // So we simply use equality to check them - return !filenameRegex.matcher(this).find() && "." != this && ".." != this -} - operator fun String.times(@IntRange(from = 0) num: Int): String { require(num >= 0) { "Param num should >= 0" @@ -168,6 +154,11 @@ fun String.onlyDigits(): String = replace(Regex("\\D*"), "") fun String.removeAllSpecialCharacters(): String = replace(Regex("[^a-zA-Z]+"), "") +/** + * Validate given text is a valid filename. + * + * @return true if given text is a valid filename + */ fun String.isValidFilename(): Boolean { val filenameRegex = Pattern.compile("[\\\\\\/:\\*\\?\"<>\\|\\x01-\\x1F\\x7F]", Pattern.CASE_INSENSITIVE) @@ -280,8 +271,6 @@ fun String?.isNetworkUrl(): Boolean = URLUtil.isNetworkUrl(this) fun String?.isValidUrl(): Boolean = URLUtil.isValidUrl(this) -fun Uri.withAppendedId(id: Long): Uri = ContentUris.withAppendedId(this, id) - // ---------------------Calculate-------------------------------// infix fun Double.plus(that: Double): Double { diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt index b12f1aef0..af1d273a9 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt @@ -20,10 +20,8 @@ import androidx.annotation.PluralsRes import androidx.annotation.Px import androidx.annotation.StringRes import androidx.core.widget.doAfterTextChanged -import androidx.lifecycle.LifecycleOwner import androidx.recyclerview.widget.AsyncDifferConfig import androidx.recyclerview.widget.DiffUtil -import androidx.viewbinding.ViewBinding import com.blankj.utilcode.util.AdaptScreenUtils import com.blankj.utilcode.util.ColorUtils import com.blankj.utilcode.util.ImageUtils @@ -32,6 +30,7 @@ import com.blankj.utilcode.util.ResourceUtils import com.blankj.utilcode.util.SPUtils import com.blankj.utilcode.util.SizeUtils import com.blankj.utilcode.util.StringUtils +import com.blankj.utilcode.util.TimeUtils import com.google.android.material.textfield.TextInputLayout import com.scwang.smart.refresh.layout.SmartRefreshLayout import io.goooler.demoapp.base.util.Dp @@ -41,7 +40,7 @@ import io.goooler.demoapp.base.util.ToastUtil import io.goooler.demoapp.common.BuildConfig import io.goooler.demoapp.common.CommonApplication import io.goooler.demoapp.common.type.SpKeys -import java.lang.reflect.ParameterizedType +import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.asExecutor From 8755e1325938ba6c3a2ed54ddef63f22f75aed05 Mon Sep 17 00:00:00 2001 From: Goooler Date: Sun, 31 Jul 2022 20:38:57 +0800 Subject: [PATCH 4/4] Cleanup --- .../kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt index af1d273a9..d4de0bb1c 100644 --- a/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt +++ b/common/src/main/kotlin/io/goooler/demoapp/common/util/CommonExtensions.kt @@ -75,15 +75,13 @@ val contentResolver: ContentResolver get() = CommonApplication.app.contentResolv val packageManager: PackageManager get() = CommonApplication.app.packageManager -// ---------------------Unit-------------------------------// +// ---------------------String-------------------------------// fun Long.formatFileSize(): String = Formatter.formatFileSize(CommonApplication.app, this) fun Long.millis2String(pattern: String = "yyyyMMddHHmmss"): String = TimeUtils.millis2String(this, DateFormat.getBestDateTimePattern(Locale.getDefault(), pattern)) -// ---------------------String-------------------------------// - @AnyThread fun String.showToast() { ToastUtil.show(CommonApplication.app, this)