parent
6448dfbf2c
commit
9dfe9842e2
@ -0,0 +1,11 @@ |
|||||||
|
package com.android.base.utils.common |
||||||
|
|
||||||
|
import java.text.SimpleDateFormat |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
fun formatMilliseconds(milliseconds: Long, pattern: String = "yyyy-MM-dd"): String { |
||||||
|
val sdp = SimpleDateFormat(pattern, Locale.getDefault()) |
||||||
|
val date = Date() |
||||||
|
date.time = milliseconds |
||||||
|
return sdp.format(date) |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
*.iml |
||||||
|
.gradle |
||||||
|
/local.properties |
||||||
|
/.idea |
||||||
|
.DS_Store |
||||||
|
/build |
||||||
|
/captures |
||||||
|
.externalNativeBuild |
||||||
|
*.iml |
||||||
|
.idea/ |
||||||
|
.gradle |
||||||
|
/local.properties |
||||||
|
.DS_Store |
||||||
|
/build |
||||||
|
/captures |
||||||
|
*.apk |
||||||
|
*.ap_ |
||||||
|
*.dex |
||||||
|
*.class |
||||||
|
bin/ |
||||||
|
gen/ |
||||||
|
local.properties |
@ -0,0 +1,3 @@ |
|||||||
|
# Explanation |
||||||
|
|
||||||
|
modifying from [kotlin-coroutines-android](https://github.com/enbandari/kotlin-coroutines-android) to support AndroidX. |
@ -0,0 +1,41 @@ |
|||||||
|
apply plugin: 'com.android.library' |
||||||
|
apply plugin: 'kotlin-android' |
||||||
|
|
||||||
|
android { |
||||||
|
compileSdkVersion rootProject.compileSdkVersion |
||||||
|
buildToolsVersion rootProject.buildToolsVersion |
||||||
|
|
||||||
|
defaultConfig { |
||||||
|
minSdkVersion rootProject.minSdkVersion |
||||||
|
targetSdkVersion rootProject.targetSdkVersion |
||||||
|
versionCode 1 |
||||||
|
versionName "1.0" |
||||||
|
} |
||||||
|
|
||||||
|
kotlinOptions { |
||||||
|
jvmTarget = "1.8" |
||||||
|
} |
||||||
|
|
||||||
|
buildTypes { |
||||||
|
release { |
||||||
|
minifyEnabled false |
||||||
|
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
dependencies { |
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar']) |
||||||
|
|
||||||
|
//Kotlin |
||||||
|
api kotlinLibraries.kotlinStdlib |
||||||
|
api kotlinLibraries.kotlinReflect |
||||||
|
api kotlinLibraries.kotlinCoroutines |
||||||
|
api kotlinLibraries.kotlinAndroidCoroutines |
||||||
|
|
||||||
|
//AndroidX |
||||||
|
compileOnly androidLibraries.appcompat |
||||||
|
compileOnly androidLibraries.recyclerView |
||||||
|
compileOnly androidLibraries.material |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
# Add project specific ProGuard rules here. |
||||||
|
# You can control the set of applied configuration files using the |
||||||
|
# proguardFiles setting in build.gradle. |
||||||
|
# |
||||||
|
# For more details, see |
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html |
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following |
||||||
|
# and specify the fully qualified class name to the JavaScript interface |
||||||
|
# class: |
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview { |
||||||
|
# public *; |
||||||
|
#} |
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for |
||||||
|
# debugging stack traces. |
||||||
|
#-keepattributes SourceFile,LineNumberTable |
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to |
||||||
|
# hide the original source file name. |
||||||
|
#-renamesourcefileattribute SourceFile |
@ -0,0 +1,2 @@ |
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" |
||||||
|
package="com.bennyhuo.kotlin.coroutines.android.mainscope"/> |
@ -0,0 +1,38 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.autodisposable |
||||||
|
|
||||||
|
import android.os.Build |
||||||
|
import android.view.View |
||||||
|
import android.view.View.OnAttachStateChangeListener |
||||||
|
import kotlinx.coroutines.* |
||||||
|
|
||||||
|
class AutoDisposableJob( |
||||||
|
private val view: View, |
||||||
|
private val wrapped: Job |
||||||
|
) : Job by wrapped, OnAttachStateChangeListener { |
||||||
|
|
||||||
|
override fun onViewAttachedToWindow(v: View?) = Unit |
||||||
|
|
||||||
|
override fun onViewDetachedFromWindow(v: View?) { |
||||||
|
cancel() |
||||||
|
view.removeOnAttachStateChangeListener(this) |
||||||
|
} |
||||||
|
|
||||||
|
private fun isViewAttached() = |
||||||
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && view.isAttachedToWindow || view.windowToken != null |
||||||
|
|
||||||
|
init { |
||||||
|
if (isViewAttached()) { |
||||||
|
view.addOnAttachStateChangeListener(this) |
||||||
|
} else { |
||||||
|
cancel() |
||||||
|
} |
||||||
|
|
||||||
|
invokeOnCompletion { |
||||||
|
view.post { |
||||||
|
view.removeOnAttachStateChangeListener(this) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun Job.asAutoDisposable(view: View) = AutoDisposableJob(view, this) |
@ -0,0 +1,16 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.autodisposable |
||||||
|
|
||||||
|
import android.view.View |
||||||
|
import kotlinx.coroutines.* |
||||||
|
import kotlin.coroutines.CoroutineContext |
||||||
|
|
||||||
|
fun View.onClickAutoDisposable( |
||||||
|
context: CoroutineContext = Dispatchers.Main, |
||||||
|
handler: suspend CoroutineScope.(v: View?) -> Unit |
||||||
|
) { |
||||||
|
setOnClickListener { v -> |
||||||
|
GlobalScope.launch(context, CoroutineStart.DEFAULT) { |
||||||
|
handler(v) |
||||||
|
}.asAutoDisposable(v) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope |
||||||
|
|
||||||
|
import android.app.Application |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.internal.ActivityLifecycleCallbackImpl |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.EmptyInterceptor |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.EmptyJob |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.ImmutableCoroutineContext |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.utils.Logcat |
||||||
|
import kotlinx.coroutines.CoroutineScope |
||||||
|
import kotlinx.coroutines.Dispatchers |
||||||
|
import kotlinx.coroutines.SupervisorJob |
||||||
|
import kotlin.coroutines.CoroutineContext |
||||||
|
|
||||||
|
interface MainScope : CoroutineScope { |
||||||
|
|
||||||
|
companion object { |
||||||
|
internal var isSetUp = false |
||||||
|
internal var isDebug = false |
||||||
|
|
||||||
|
val isFragmentSupported by lazy { |
||||||
|
try { |
||||||
|
Class.forName("androidx.fragment.app.FragmentManager\$FragmentLifecycleCallbacks") |
||||||
|
Logcat.debug("Fragment enabled.") |
||||||
|
true |
||||||
|
} catch (e: ClassNotFoundException) { |
||||||
|
Logcat.debug("Fragment disabled.") |
||||||
|
Logcat.error(e) |
||||||
|
false |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun setUp(application: Application): Companion { |
||||||
|
application.registerActivityLifecycleCallbacks(ActivityLifecycleCallbackImpl) |
||||||
|
isSetUp = true |
||||||
|
return this |
||||||
|
} |
||||||
|
|
||||||
|
fun enableDebug() { |
||||||
|
isDebug = true |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
internal class MainScopeImpl : MainScope { |
||||||
|
override val coroutineContext = SupervisorJob() + Dispatchers.Main |
||||||
|
} |
||||||
|
|
||||||
|
internal object EmptyScope : MainScope { |
||||||
|
override val coroutineContext: CoroutineContext = ImmutableCoroutineContext(EmptyJob() + EmptyInterceptor) |
||||||
|
} |
@ -0,0 +1,5 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.exception |
||||||
|
|
||||||
|
class UnsupportedTypeException(type: Class<*>, vararg supportedTypes: String) : Exception("Unsupported type: $type. ${supportedTypes.joinToString()} ${if (supportedTypes.size == 1) "is" else "are"} needed.") |
||||||
|
|
||||||
|
class UnsupportedVersionException(library: String, version: String) : Exception("Unsupported version: $version of $library") |
@ -0,0 +1,46 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.internal |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.app.Application |
||||||
|
import android.os.Bundle |
||||||
|
import androidx.fragment.app.FragmentActivity |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.MainScoped |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.onMainScopeCreate |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.onMainScopeDestroy |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.utils.Logcat |
||||||
|
|
||||||
|
internal object ActivityLifecycleCallbackImpl : Application.ActivityLifecycleCallbacks { |
||||||
|
|
||||||
|
override fun onActivityPaused(activity: Activity) {} |
||||||
|
|
||||||
|
override fun onActivityResumed(activity: Activity) {} |
||||||
|
|
||||||
|
override fun onActivityStarted(activity: Activity) {} |
||||||
|
|
||||||
|
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle?) {} |
||||||
|
|
||||||
|
override fun onActivityStopped(activity: Activity) {} |
||||||
|
|
||||||
|
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) { |
||||||
|
(activity as? MainScoped)?.onMainScopeCreate() |
||||||
|
if (MainScope.isFragmentSupported) { |
||||||
|
(activity as? FragmentActivity)?.supportFragmentManager?.registerFragmentLifecycleCallbacks(FragmentLifecycleCallbackImpl, true) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
override fun onActivityDestroyed(activity: Activity) { |
||||||
|
(activity as? MainScoped)?.onMainScopeDestroy() |
||||||
|
if (MainScope.isFragmentSupported) { |
||||||
|
Logcat.debug("onActivityDestroyed") |
||||||
|
(activity as? FragmentActivity)?.supportFragmentManager?.let { fragmentManager -> |
||||||
|
fragmentManager.unregisterFragmentLifecycleCallbacks(FragmentLifecycleCallbackImpl) |
||||||
|
//Fragments may not be destroyed, so cancel scope right now. |
||||||
|
fragmentManager.fragments.forEach { fragment -> |
||||||
|
(fragment as? MainScoped)?.onMainScopeDestroy() |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.internal |
||||||
|
|
||||||
|
import android.os.Bundle |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import androidx.fragment.app.FragmentManager |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.MainScoped |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.onMainScopeCreate |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.scope.onMainScopeDestroy |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.utils.Logcat |
||||||
|
|
||||||
|
internal object FragmentLifecycleCallbackImpl: FragmentManager.FragmentLifecycleCallbacks() { |
||||||
|
|
||||||
|
override fun onFragmentCreated(fm: FragmentManager, f: Fragment, savedInstanceState: Bundle?) { |
||||||
|
super.onFragmentCreated(fm, f, savedInstanceState) |
||||||
|
(f as? MainScoped)?.onMainScopeCreate() |
||||||
|
} |
||||||
|
|
||||||
|
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) { |
||||||
|
super.onFragmentViewDestroyed(fm, f) |
||||||
|
Logcat.debug("onFragmentViewDestroyed") |
||||||
|
(f as? MainScoped)?.onMainScopeDestroy() |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job |
||||||
|
|
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import kotlinx.coroutines.CoroutineStart |
||||||
|
import kotlinx.coroutines.Job |
||||||
|
import kotlinx.coroutines.newCoroutineContext |
||||||
|
import kotlin.coroutines.CoroutineContext |
||||||
|
import kotlin.coroutines.EmptyCoroutineContext |
||||||
|
import kotlinx.coroutines.launch as launchInternal |
||||||
|
|
||||||
|
|
||||||
|
internal fun MainScope.launch( |
||||||
|
context: CoroutineContext = EmptyCoroutineContext, |
||||||
|
start: CoroutineStart = CoroutineStart.DEFAULT, |
||||||
|
block: suspend MainScope.() -> Unit |
||||||
|
): Job { |
||||||
|
val newContext = newCoroutineContext(context) |
||||||
|
val coroutine = StandaloneCoroutineCompat(newContext, true) |
||||||
|
coroutine.start(start, coroutine, block) |
||||||
|
return coroutine |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job |
||||||
|
|
||||||
|
import kotlinx.coroutines.DisposableHandle |
||||||
|
|
||||||
|
object EmptyDisposableHandle: DisposableHandle { |
||||||
|
override fun dispose() = Unit |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job |
||||||
|
|
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.utils.Logcat |
||||||
|
import kotlin.coroutines.* |
||||||
|
|
||||||
|
internal object EmptyInterceptor: ContinuationInterceptor, AbstractCoroutineContextElement(ContinuationInterceptor) { |
||||||
|
|
||||||
|
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> { |
||||||
|
return EmptyContinuation as Continuation<T> |
||||||
|
} |
||||||
|
|
||||||
|
private object EmptyContinuation: Continuation<Any>{ |
||||||
|
override val context: CoroutineContext = EmptyCoroutineContext |
||||||
|
|
||||||
|
override fun resumeWith(result: Result<Any>) { |
||||||
|
Logcat.warn("Intercepted coroutine. won't resume.") |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job |
||||||
|
|
||||||
|
import kotlinx.coroutines.CancellationException |
||||||
|
import kotlinx.coroutines.CompletionHandler |
||||||
|
import kotlinx.coroutines.InternalCoroutinesApi |
||||||
|
import kotlinx.coroutines.Job |
||||||
|
import kotlinx.coroutines.selects.SelectClause0 |
||||||
|
import kotlinx.coroutines.selects.SelectInstance |
||||||
|
import kotlin.coroutines.CoroutineContext |
||||||
|
import kotlin.coroutines.CoroutineContext.Element |
||||||
|
import kotlin.coroutines.CoroutineContext.Key |
||||||
|
import kotlin.coroutines.EmptyCoroutineContext |
||||||
|
|
||||||
|
internal class EmptyJob : JobCompat(), SelectClause0 { |
||||||
|
|
||||||
|
private val cancellationException = CancellationException("EmptyScope") |
||||||
|
|
||||||
|
override val children: Sequence<Job> = emptySequence() |
||||||
|
|
||||||
|
override val isActive: Boolean = false |
||||||
|
|
||||||
|
override val isCancelled: Boolean = true |
||||||
|
|
||||||
|
override val isCompleted: Boolean = true |
||||||
|
|
||||||
|
override val key: Key<*> = Job |
||||||
|
|
||||||
|
override val onJoin: SelectClause0 = this |
||||||
|
|
||||||
|
@InternalCoroutinesApi |
||||||
|
override fun <R> registerSelectClause0(select: SelectInstance<R>, block: suspend () -> R) = Unit |
||||||
|
|
||||||
|
override fun cancel(cause: Throwable?) = false |
||||||
|
|
||||||
|
override fun cancel(cause: CancellationException?) = Unit |
||||||
|
|
||||||
|
@InternalCoroutinesApi |
||||||
|
override fun getCancellationException() = cancellationException |
||||||
|
|
||||||
|
@InternalCoroutinesApi |
||||||
|
override fun invokeOnCompletion(onCancelling: Boolean, invokeImmediately: Boolean, handler: CompletionHandler) = EmptyDisposableHandle.also { handler.invoke(cancellationException) } |
||||||
|
|
||||||
|
override fun invokeOnCompletion(handler: CompletionHandler) = EmptyDisposableHandle.also { handler.invoke(cancellationException) } |
||||||
|
|
||||||
|
override suspend fun join() = Unit |
||||||
|
|
||||||
|
override fun start() = false |
||||||
|
|
||||||
|
override fun plus(other: Job): Job = this |
||||||
|
|
||||||
|
override fun cancel() = Unit |
||||||
|
|
||||||
|
//region solutions for AbstractMethodError. |
||||||
|
override operator fun <E : Element> get(key: Key<E>): E? = |
||||||
|
if (this.key == key) this as E else null |
||||||
|
|
||||||
|
override fun <R> fold(initial: R, operation: (R, Element) -> R): R = operation(initial, this) |
||||||
|
|
||||||
|
override fun minusKey(key: Key<*>): CoroutineContext = |
||||||
|
if (this.key == key) EmptyCoroutineContext else this |
||||||
|
//endregion |
||||||
|
|
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job |
||||||
|
|
||||||
|
import kotlin.coroutines.CoroutineContext |
||||||
|
import kotlin.coroutines.CoroutineContext.Element |
||||||
|
import kotlin.coroutines.CoroutineContext.Key |
||||||
|
|
||||||
|
class ImmutableCoroutineContext(private val coroutineContext: CoroutineContext): CoroutineContext { |
||||||
|
override fun <R> fold(initial: R, operation: (R, Element) -> R): R = coroutineContext.fold(initial, operation) |
||||||
|
|
||||||
|
override fun <E : Element> get(key: Key<E>) = coroutineContext[key] |
||||||
|
|
||||||
|
override fun minusKey(key: Key<*>) = this |
||||||
|
|
||||||
|
override fun plus(context: CoroutineContext) = this |
||||||
|
} |
@ -0,0 +1,37 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import kotlin.coroutines.CoroutineContext; |
||||||
|
import kotlinx.coroutines.ChildHandle; |
||||||
|
import kotlinx.coroutines.ChildJob; |
||||||
|
import kotlinx.coroutines.Job; |
||||||
|
|
||||||
|
abstract class JobCompat implements Job { |
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public final ChildHandle attachChild(@NotNull ChildJob childJob) { |
||||||
|
//Parent is already cancelled. So cancel child directly.
|
||||||
|
childJob.cancel(getCancellationException()); |
||||||
|
return EmptyChildHandle.instance; |
||||||
|
} |
||||||
|
|
||||||
|
private static class EmptyChildHandle implements ChildHandle { |
||||||
|
private static final EmptyChildHandle instance = new EmptyChildHandle(); |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean childCancelled(@NotNull Throwable throwable) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void dispose() { } |
||||||
|
} |
||||||
|
|
||||||
|
//solutions for AbstractMethodError.
|
||||||
|
@NotNull |
||||||
|
@Override |
||||||
|
public final CoroutineContext plus(@NotNull CoroutineContext coroutineContext) { |
||||||
|
return CoroutineContext.DefaultImpls.plus(this, coroutineContext); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.job; |
||||||
|
|
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope; |
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull; |
||||||
|
|
||||||
|
import kotlin.Unit; |
||||||
|
import kotlin.coroutines.CoroutineContext; |
||||||
|
import kotlinx.coroutines.AbstractCoroutine; |
||||||
|
import kotlinx.coroutines.CoroutineExceptionHandlerKt; |
||||||
|
|
||||||
|
class StandaloneCoroutineCompat extends AbstractCoroutine<Unit> implements MainScope { |
||||||
|
public StandaloneCoroutineCompat(@NotNull CoroutineContext parentContext, boolean active) { |
||||||
|
super(parentContext, active); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected boolean handleJobException(@NotNull Throwable exception) { |
||||||
|
CoroutineExceptionHandlerKt.handleCoroutineException(getContext(), exception); |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,195 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.scope |
||||||
|
|
||||||
|
import androidx.appcompat.widget.* |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.launch |
||||||
|
|
||||||
|
interface AppCompatScoped : BasicScoped { |
||||||
|
|
||||||
|
fun ActionMenuView.onMenuItemClick( |
||||||
|
returnValue: Boolean = false, |
||||||
|
handler: suspend MainScope.(item: android.view.MenuItem?) -> Unit |
||||||
|
) { |
||||||
|
setOnMenuItemClickListener { item -> |
||||||
|
mainScope.launch { |
||||||
|
handler(item) |
||||||
|
} |
||||||
|
returnValue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun ActivityChooserView.onDismiss( |
||||||
|
handler: suspend MainScope.() -> Unit |
||||||
|
) { |
||||||
|
setOnDismissListener { -> |
||||||
|
mainScope.launch(block = handler) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun FitWindowsFrameLayout.onFitSystemWindows( |
||||||
|
handler: suspend MainScope.(insets: android.graphics.Rect?) -> Unit |
||||||
|
) { |
||||||
|
setOnFitSystemWindowsListener { insets -> |
||||||
|
mainScope.launch { |
||||||
|
handler(insets) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun SearchView.onClose( |
||||||
|
returnValue: Boolean = false, |
||||||
|
handler: suspend MainScope.() -> Unit |
||||||
|
) { |
||||||
|
setOnCloseListener { -> |
||||||
|
mainScope.launch(block = handler) |
||||||
|
returnValue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun SearchView.onQueryTextFocusChange( |
||||||
|
handler: suspend MainScope.(v: android.view.View, hasFocus: Boolean) -> Unit |
||||||
|
) { |
||||||
|
setOnQueryTextFocusChangeListener { v, hasFocus -> |
||||||
|
mainScope.launch { |
||||||
|
handler(v, hasFocus) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun SearchView.onQueryTextListener( |
||||||
|
init: __SearchView_OnQueryTextListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __SearchView_OnQueryTextListener(mainScope) |
||||||
|
listener.init() |
||||||
|
setOnQueryTextListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __SearchView_OnQueryTextListener(private val mainScope: MainScope) : SearchView.OnQueryTextListener { |
||||||
|
|
||||||
|
private var _onQueryTextSubmit: (suspend MainScope.(String?) -> Boolean)? = null |
||||||
|
private var _onQueryTextSubmit_returnValue: Boolean = false |
||||||
|
|
||||||
|
override fun onQueryTextSubmit(query: String?): Boolean { |
||||||
|
val returnValue = _onQueryTextSubmit_returnValue |
||||||
|
val handler = _onQueryTextSubmit ?: return returnValue |
||||||
|
mainScope.launch { |
||||||
|
handler(query) |
||||||
|
} |
||||||
|
return returnValue |
||||||
|
} |
||||||
|
|
||||||
|
fun onQueryTextSubmit( |
||||||
|
returnValue: Boolean = false, |
||||||
|
listener: suspend MainScope.(String?) -> Boolean |
||||||
|
) { |
||||||
|
_onQueryTextSubmit = listener |
||||||
|
_onQueryTextSubmit_returnValue = returnValue |
||||||
|
} |
||||||
|
|
||||||
|
private var _onQueryTextChange: (suspend MainScope.(String?) -> Boolean)? = null |
||||||
|
private var _onQueryTextChange_returnValue: Boolean = false |
||||||
|
|
||||||
|
override fun onQueryTextChange(newText: String?): Boolean { |
||||||
|
val returnValue = _onQueryTextChange_returnValue |
||||||
|
val handler = _onQueryTextChange ?: return returnValue |
||||||
|
mainScope.launch { |
||||||
|
handler(newText) |
||||||
|
} |
||||||
|
return returnValue |
||||||
|
} |
||||||
|
|
||||||
|
fun onQueryTextChange( |
||||||
|
returnValue: Boolean = false, |
||||||
|
listener: suspend MainScope.(String?) -> Boolean |
||||||
|
) { |
||||||
|
_onQueryTextChange = listener |
||||||
|
_onQueryTextChange_returnValue = returnValue |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun SearchView.onSearchClick( |
||||||
|
handler: suspend MainScope.(v: android.view.View?) -> Unit |
||||||
|
) { |
||||||
|
setOnSearchClickListener { v -> |
||||||
|
mainScope.launch { |
||||||
|
handler(v) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun SearchView.onSuggestionListener( |
||||||
|
init: __SearchView_OnSuggestionListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __SearchView_OnSuggestionListener(mainScope) |
||||||
|
listener.init() |
||||||
|
setOnSuggestionListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __SearchView_OnSuggestionListener(private val mainScope: MainScope) : SearchView.OnSuggestionListener { |
||||||
|
|
||||||
|
private var _onSuggestionSelect: (suspend MainScope.(Int) -> Boolean)? = null |
||||||
|
private var _onSuggestionSelect_returnValue: Boolean = false |
||||||
|
|
||||||
|
override fun onSuggestionSelect(position: Int): Boolean { |
||||||
|
val returnValue = _onSuggestionSelect_returnValue |
||||||
|
val handler = _onSuggestionSelect ?: return returnValue |
||||||
|
mainScope.launch { |
||||||
|
handler(position) |
||||||
|
} |
||||||
|
return returnValue |
||||||
|
} |
||||||
|
|
||||||
|
fun onSuggestionSelect( |
||||||
|
returnValue: Boolean = false, |
||||||
|
listener: suspend MainScope.(Int) -> Boolean |
||||||
|
) { |
||||||
|
_onSuggestionSelect = listener |
||||||
|
_onSuggestionSelect_returnValue = returnValue |
||||||
|
} |
||||||
|
|
||||||
|
private var _onSuggestionClick: (suspend MainScope.(Int) -> Boolean)? = null |
||||||
|
private var _onSuggestionClick_returnValue: Boolean = false |
||||||
|
|
||||||
|
override fun onSuggestionClick(position: Int): Boolean { |
||||||
|
val returnValue = _onSuggestionClick_returnValue |
||||||
|
val handler = _onSuggestionClick ?: return returnValue |
||||||
|
mainScope.launch { |
||||||
|
handler(position) |
||||||
|
} |
||||||
|
return returnValue |
||||||
|
} |
||||||
|
|
||||||
|
fun onSuggestionClick( |
||||||
|
returnValue: Boolean = false, |
||||||
|
listener: suspend MainScope.(Int) -> Boolean |
||||||
|
) { |
||||||
|
_onSuggestionClick = listener |
||||||
|
_onSuggestionClick_returnValue = returnValue |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun Toolbar.onMenuItemClick( |
||||||
|
returnValue: Boolean = false, |
||||||
|
handler: suspend MainScope.(item: android.view.MenuItem?) -> Unit |
||||||
|
) { |
||||||
|
setOnMenuItemClickListener { item -> |
||||||
|
mainScope.launch { |
||||||
|
handler(item) |
||||||
|
} |
||||||
|
returnValue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun ViewStubCompat.onInflate( |
||||||
|
handler: suspend MainScope.(stub: ViewStubCompat?, inflated: android.view.View?) -> Unit |
||||||
|
) { |
||||||
|
setOnInflateListener { stub, inflated -> |
||||||
|
mainScope.launch { |
||||||
|
handler(stub, inflated) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,369 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.scope |
||||||
|
|
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.launch |
||||||
|
import com.google.android.material.appbar.AppBarLayout |
||||||
|
import com.google.android.material.bottomnavigation.BottomNavigationView |
||||||
|
import com.google.android.material.chip.ChipGroup |
||||||
|
import com.google.android.material.floatingactionbutton.FloatingActionButton |
||||||
|
import com.google.android.material.tabs.TabLayout |
||||||
|
|
||||||
|
interface DesignScoped : BasicScoped { |
||||||
|
|
||||||
|
fun AppBarLayout.onOffsetChanged( |
||||||
|
handler: suspend MainScope.(appBarLayout: AppBarLayout?, verticalOffset: Int) -> Unit |
||||||
|
) { |
||||||
|
addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> |
||||||
|
mainScope.launch { |
||||||
|
handler(appBarLayout, verticalOffset) |
||||||
|
} |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
fun <T : TabLayout.Tab> TabLayout.onTabSelectedListener( |
||||||
|
init: __onTabSelected_TabLayout_BaseOnTabSelectedListener<T>.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __onTabSelected_TabLayout_BaseOnTabSelectedListener<T>(mainScope) |
||||||
|
listener.init() |
||||||
|
addOnTabSelectedListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __onTabSelected_TabLayout_BaseOnTabSelectedListener<T : TabLayout.Tab>(private val mainScope: MainScope) : TabLayout.BaseOnTabSelectedListener<T> { |
||||||
|
|
||||||
|
private var _onTabSelectedWithP0: (suspend MainScope.(T?) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onTabSelected(p0: T?) { |
||||||
|
val handler = _onTabSelectedWithP0 ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(p0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onTabSelected( |
||||||
|
listener: suspend MainScope.(T?) -> Unit |
||||||
|
) { |
||||||
|
_onTabSelectedWithP0 = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onTabUnselectedWithP0: (suspend MainScope.(T?) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onTabUnselected(p0: T?) { |
||||||
|
val handler = _onTabUnselectedWithP0 ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(p0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onTabUnselected( |
||||||
|
listener: suspend MainScope.(T?) -> Unit |
||||||
|
) { |
||||||
|
_onTabUnselectedWithP0 = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onTabReselectedWithP0: (suspend MainScope.(T?) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onTabReselected(p0: T?) { |
||||||
|
val handler = _onTabReselectedWithP0 ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(p0) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onTabReselected( |
||||||
|
listener: suspend MainScope.(T?) -> Unit |
||||||
|
) { |
||||||
|
_onTabReselectedWithP0 = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun FloatingActionButton.onShowAnimationListener( |
||||||
|
init: __onShowAnimation_Animator_AnimatorListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __onShowAnimation_Animator_AnimatorListener(mainScope) |
||||||
|
listener.init() |
||||||
|
addOnShowAnimationListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __onShowAnimation_Animator_AnimatorListener(private val mainScope: MainScope) : android.animation.Animator.AnimatorListener { |
||||||
|
|
||||||
|
private var _onAnimationStartWithAnimationAndIsReverse: (suspend MainScope.(android.animation.Animator?, Boolean) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onAnimationStart(animation: android.animation.Animator?, isReverse: Boolean) { |
||||||
|
val handler = _onAnimationStartWithAnimationAndIsReverse ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation, isReverse) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationStart( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?, Boolean) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationStartWithAnimationAndIsReverse = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationEndWithAnimationAndIsReverse: (suspend MainScope.(android.animation.Animator?, Boolean) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: android.animation.Animator?, isReverse: Boolean) { |
||||||
|
val handler = _onAnimationEndWithAnimationAndIsReverse ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation, isReverse) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationEnd( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?, Boolean) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationEndWithAnimationAndIsReverse = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationStartWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationStart(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationStartWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationStart( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationStartWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationEndWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationEndWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationEnd( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationEndWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationCancelWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationCancel(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationCancelWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationCancel( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationCancelWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationRepeatWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationRepeatWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationRepeat( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationRepeatWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun FloatingActionButton.onHideAnimationListener( |
||||||
|
init: __onHideAnimation_Animator_AnimatorListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __onHideAnimation_Animator_AnimatorListener(mainScope) |
||||||
|
listener.init() |
||||||
|
addOnHideAnimationListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __onHideAnimation_Animator_AnimatorListener(private val mainScope: MainScope) : android.animation.Animator.AnimatorListener { |
||||||
|
|
||||||
|
private var _onAnimationStartWithAnimationAndIsReverse: (suspend MainScope.(android.animation.Animator?, Boolean) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationStart(animation: android.animation.Animator?, isReverse: Boolean) { |
||||||
|
val handler = _onAnimationStartWithAnimationAndIsReverse ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation, isReverse) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationStart( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?, Boolean) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationStartWithAnimationAndIsReverse = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationEndWithAnimationAndIsReverse: (suspend MainScope.(android.animation.Animator?, Boolean) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: android.animation.Animator?, isReverse: Boolean) { |
||||||
|
val handler = _onAnimationEndWithAnimationAndIsReverse ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation, isReverse) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationEnd( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?, Boolean) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationEndWithAnimationAndIsReverse = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationStartWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationStart(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationStartWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationStart( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationStartWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationEndWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationEnd(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationEndWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationEnd( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationEndWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationCancelWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationCancel(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationCancelWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationCancel( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationCancelWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onAnimationRepeatWithAnimation: (suspend MainScope.(android.animation.Animator?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onAnimationRepeat(animation: android.animation.Animator?) { |
||||||
|
val handler = _onAnimationRepeatWithAnimation ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(animation) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onAnimationRepeat( |
||||||
|
listener: suspend MainScope.(android.animation.Animator?) -> Unit |
||||||
|
) { |
||||||
|
_onAnimationRepeatWithAnimation = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun ChipGroup.onCheckedChange( |
||||||
|
handler: suspend MainScope.(p0: ChipGroup?, p1: Int) -> Unit |
||||||
|
) { |
||||||
|
setOnCheckedChangeListener { p0, p1 -> |
||||||
|
mainScope.launch { |
||||||
|
handler(p0, p1) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun ChipGroup.onHierarchyChangeListener( |
||||||
|
init: __onHierarchyChange_ViewGroup_OnHierarchyChangeListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __onHierarchyChange_ViewGroup_OnHierarchyChangeListener(mainScope) |
||||||
|
listener.init() |
||||||
|
setOnHierarchyChangeListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __onHierarchyChange_ViewGroup_OnHierarchyChangeListener(private val mainScope: MainScope) : android.view.ViewGroup.OnHierarchyChangeListener { |
||||||
|
|
||||||
|
private var _onChildViewAddedWithParentAndChild: (suspend MainScope.(android.view.View?, android.view.View?) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onChildViewAdded(parent: android.view.View?, child: android.view.View?) { |
||||||
|
val handler = _onChildViewAddedWithParentAndChild ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(parent, child) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onChildViewAdded( |
||||||
|
listener: suspend MainScope.(android.view.View?, android.view.View?) -> Unit |
||||||
|
) { |
||||||
|
_onChildViewAddedWithParentAndChild = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onChildViewRemovedWithParentAndChild: (suspend MainScope.(android.view.View?, android.view.View?) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onChildViewRemoved(parent: android.view.View?, child: android.view.View?) { |
||||||
|
val handler = _onChildViewRemovedWithParentAndChild ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(parent, child) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onChildViewRemoved( |
||||||
|
listener: suspend MainScope.(android.view.View?, android.view.View?) -> Unit |
||||||
|
) { |
||||||
|
_onChildViewRemovedWithParentAndChild = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun BottomNavigationView.onNavigationItemReselected( |
||||||
|
handler: suspend MainScope.(item: android.view.MenuItem) -> Unit |
||||||
|
) { |
||||||
|
setOnNavigationItemReselectedListener { item -> |
||||||
|
mainScope.launch { |
||||||
|
handler(item) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun BottomNavigationView.onNavigationItemSelected( |
||||||
|
returnValue: Boolean = false, |
||||||
|
handler: suspend MainScope.(item: android.view.MenuItem) -> Unit |
||||||
|
) { |
||||||
|
setOnNavigationItemSelectedListener { item -> |
||||||
|
mainScope.launch { |
||||||
|
handler(item) |
||||||
|
} |
||||||
|
returnValue |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,73 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.scope |
||||||
|
|
||||||
|
import android.app.Activity |
||||||
|
import android.os.Looper |
||||||
|
import androidx.fragment.app.Fragment |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.EmptyScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScopeImpl |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.exception.UnsupportedTypeException |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.exception.UnsupportedVersionException |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.utils.Logcat |
||||||
|
import kotlinx.coroutines.cancel |
||||||
|
import java.util.* |
||||||
|
|
||||||
|
interface MainScoped { |
||||||
|
|
||||||
|
companion object { |
||||||
|
internal val scopeMap = IdentityHashMap<MainScoped, MainScope>() |
||||||
|
} |
||||||
|
|
||||||
|
val mainScope: MainScope |
||||||
|
get() { |
||||||
|
if(!MainScope.isSetUp){ |
||||||
|
throw IllegalStateException("MainScope has not been set up yet! Call `MainScope.setUp(application)` once your customized Application created.") |
||||||
|
} |
||||||
|
if(Thread.currentThread() != Looper.getMainLooper().thread){ |
||||||
|
throw IllegalAccessException("MainScope must be accessed from the UI main thread.") |
||||||
|
} |
||||||
|
return (scopeMap[this]) ?: run { |
||||||
|
if(isDestroyed()){ |
||||||
|
Logcat.debug("Access MainScope when scoped instance:$this is FINISHING. EmptyScope will be returned.") |
||||||
|
EmptyScope |
||||||
|
} else { |
||||||
|
Logcat.warn("Create MainScope for scoped instance: $this") |
||||||
|
MainScopeImpl().also { scopeMap[this] = it } |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private fun MainScoped.isDestroyed(): Boolean { |
||||||
|
return when{ |
||||||
|
this is Activity ->{ |
||||||
|
this.isFinishing |
||||||
|
} |
||||||
|
MainScope.isFragmentSupported && this is Fragment ->{ |
||||||
|
this.activity?.isFinishing?: true || this.isRemoving ||this.view == null |
||||||
|
} |
||||||
|
else ->{ |
||||||
|
val fragmentClass = try { |
||||||
|
Class.forName("android.support.v4.app.Fragment") |
||||||
|
} catch (e: Exception) { |
||||||
|
null |
||||||
|
} |
||||||
|
fragmentClass?.let { |
||||||
|
if(it.isAssignableFrom(this.javaClass)){ |
||||||
|
throw UnsupportedVersionException("com.android.support:support-fragment", "<25.1.0") |
||||||
|
} |
||||||
|
} |
||||||
|
throw UnsupportedTypeException(this.javaClass, "android.app.Activity", "android.support.v4.app.Fragment") |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
inline fun <T> MainScoped.withMainScope(block: MainScope.() -> T) = with(mainScope, block) |
||||||
|
|
||||||
|
internal fun MainScoped.onMainScopeCreate() { |
||||||
|
//won't create immediately. |
||||||
|
} |
||||||
|
|
||||||
|
internal fun MainScoped.onMainScopeDestroy() { |
||||||
|
MainScoped.scopeMap.remove(this)?.cancel() |
||||||
|
} |
@ -0,0 +1,115 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.scope |
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.job.launch |
||||||
|
|
||||||
|
interface RecyclerViewScoped : BasicScoped { |
||||||
|
|
||||||
|
fun RecyclerView.onChildAttachStateChangeListener( |
||||||
|
init: __RecyclerView_OnChildAttachStateChangeListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __RecyclerView_OnChildAttachStateChangeListener(mainScope) |
||||||
|
listener.init() |
||||||
|
addOnChildAttachStateChangeListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __RecyclerView_OnChildAttachStateChangeListener(private val mainScope: MainScope) : RecyclerView.OnChildAttachStateChangeListener { |
||||||
|
|
||||||
|
private var _onChildViewAttachedToWindow: (suspend MainScope.(android.view.View) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onChildViewAttachedToWindow(view: android.view.View) { |
||||||
|
val handler = _onChildViewAttachedToWindow ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(view) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onChildViewAttachedToWindow( |
||||||
|
listener: suspend MainScope.(android.view.View) -> Unit |
||||||
|
) { |
||||||
|
_onChildViewAttachedToWindow = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onChildViewDetachedFromWindow: (suspend MainScope.(android.view.View) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onChildViewDetachedFromWindow(view: android.view.View) { |
||||||
|
val handler = _onChildViewDetachedFromWindow ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(view) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onChildViewDetachedFromWindow( |
||||||
|
listener: suspend MainScope.(android.view.View) -> Unit |
||||||
|
) { |
||||||
|
_onChildViewDetachedFromWindow = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
fun RecyclerView.onItemTouchListener( |
||||||
|
init: __RecyclerView_OnItemTouchListener.() -> Unit |
||||||
|
) { |
||||||
|
val listener = __RecyclerView_OnItemTouchListener(mainScope) |
||||||
|
listener.init() |
||||||
|
addOnItemTouchListener(listener) |
||||||
|
} |
||||||
|
|
||||||
|
class __RecyclerView_OnItemTouchListener(private val mainScope: MainScope) : RecyclerView.OnItemTouchListener { |
||||||
|
|
||||||
|
private var _onInterceptTouchEvent: (suspend MainScope.(RecyclerView, android.view.MotionEvent) -> Boolean)? = null |
||||||
|
private var _onInterceptTouchEvent_returnValue: Boolean = false |
||||||
|
|
||||||
|
override fun onInterceptTouchEvent(rv: RecyclerView, e: android.view.MotionEvent): Boolean { |
||||||
|
val returnValue = _onInterceptTouchEvent_returnValue |
||||||
|
val handler = _onInterceptTouchEvent ?: return returnValue |
||||||
|
mainScope.launch { |
||||||
|
handler(rv, e) |
||||||
|
} |
||||||
|
return returnValue |
||||||
|
} |
||||||
|
|
||||||
|
fun onInterceptTouchEvent( |
||||||
|
returnValue: Boolean = false, |
||||||
|
listener: suspend MainScope.(RecyclerView, android.view.MotionEvent) -> Boolean |
||||||
|
) { |
||||||
|
_onInterceptTouchEvent = listener |
||||||
|
_onInterceptTouchEvent_returnValue = returnValue |
||||||
|
} |
||||||
|
|
||||||
|
private var _onTouchEvent: (suspend MainScope.(RecyclerView, android.view.MotionEvent) -> Unit)? = null |
||||||
|
|
||||||
|
override fun onTouchEvent(rv: RecyclerView, e: android.view.MotionEvent) { |
||||||
|
val handler = _onTouchEvent ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(rv, e) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onTouchEvent( |
||||||
|
listener: suspend MainScope.(RecyclerView, android.view.MotionEvent) -> Unit |
||||||
|
) { |
||||||
|
_onTouchEvent = listener |
||||||
|
} |
||||||
|
|
||||||
|
private var _onRequestDisallowInterceptTouchEvent: (suspend MainScope.(Boolean) -> Unit)? = null |
||||||
|
|
||||||
|
|
||||||
|
override fun onRequestDisallowInterceptTouchEvent(disallowIntercept: Boolean) { |
||||||
|
val handler = _onRequestDisallowInterceptTouchEvent ?: return |
||||||
|
mainScope.launch { |
||||||
|
handler(disallowIntercept) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fun onRequestDisallowInterceptTouchEvent( |
||||||
|
listener: suspend MainScope.(Boolean) -> Unit |
||||||
|
) { |
||||||
|
_onRequestDisallowInterceptTouchEvent = listener |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package com.bennyhuo.kotlin.coroutines.android.mainscope.utils |
||||||
|
|
||||||
|
import android.util.Log |
||||||
|
import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope |
||||||
|
|
||||||
|
object Logcat { |
||||||
|
|
||||||
|
private const val TAG = "MainScope" |
||||||
|
|
||||||
|
fun debug(log: Any?) = MainScope.isDebug.whenTrue { Log.d(TAG, log.toString()) } |
||||||
|
|
||||||
|
fun warn(log: Any?) = MainScope.isDebug.whenTrue { Log.w(TAG, log.toString()) } |
||||||
|
|
||||||
|
fun error(log: Any?) = MainScope.isDebug.whenTrue { Log.e(TAG, log.toString()) } |
||||||
|
|
||||||
|
private fun Boolean.whenTrue(block: () -> Unit) { |
||||||
|
if (this) { |
||||||
|
block() |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue