diff --git a/lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java b/lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java index e2249c9..2342e84 100644 --- a/lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java +++ b/lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java @@ -11,6 +11,8 @@ import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; /** + * {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. + * * @author Ztiany * Email: ztiany3@gmail.com * Date : 2018-11-05 14:31 diff --git a/lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java b/lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java index 81d0159..aa4cf21 100644 --- a/lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java +++ b/lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java @@ -5,7 +5,7 @@ import dagger.Binds; import dagger.Module; /** - * 使用 ViewModelModule 情况下,所有的 ViewModule 都由 Activity 界别容器提供,因此 Fragment 级容器无法为其 ViewModule 提供依赖。这是仅有的局限性。 + * {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported. * * @author Ztiany * Email: ztiany3@gmail.com diff --git a/lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt b/lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt index 49e6864..9ac772d 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt +++ b/lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt @@ -204,15 +204,16 @@ open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPr return false } - private fun loadingView(): LoadingView? { + private fun loadingView(): LoadingView { val loadingViewImpl = loadingView - if (loadingViewImpl == null) { + return if (loadingViewImpl != null) { + loadingViewImpl + } else { loadingView = onCreateLoadingView() + ?: Sword.get().loadingViewFactory.createLoadingDelegate(requireContext()) + loadingView + ?: throw NullPointerException("you need to config LoadingViewFactory") } - if (loadingViewImpl == null) { - loadingView = Sword.get().loadingViewFactory.createLoadingDelegate(requireContext()) - } - return loadingViewImpl } protected open fun onCreateLoadingView(): LoadingView? { @@ -220,31 +221,31 @@ open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPr } override fun showLoadingDialog() { - loadingView()?.showLoadingDialog(true) + loadingView().showLoadingDialog(true) } override fun showLoadingDialog(cancelable: Boolean) { - loadingView()?.showLoadingDialog(cancelable) + loadingView().showLoadingDialog(cancelable) } override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { - loadingView()?.showLoadingDialog(message, cancelable) + loadingView().showLoadingDialog(message, cancelable) } override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { - loadingView()?.showLoadingDialog(messageId, cancelable) + loadingView().showLoadingDialog(messageId, cancelable) } override fun dismissLoadingDialog() { - loadingView()?.dismissLoadingDialog() + loadingView().dismissLoadingDialog() } override fun showMessage(message: CharSequence) { - loadingView()?.showMessage(message) + loadingView().showMessage(message) } override fun showMessage(@StringRes messageId: Int) { - loadingView()?.showMessage(messageId) + loadingView().showMessage(messageId) } } \ No newline at end of file diff --git a/lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt b/lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt index d28fa79..870a0da 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt +++ b/lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt @@ -215,15 +215,16 @@ open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, Fragment return false } - private fun loadingView(): LoadingView? { + private fun loadingView(): LoadingView { val loadingViewImpl = loadingView - if (loadingViewImpl == null) { + return if (loadingViewImpl != null) { + loadingViewImpl + } else { loadingView = onCreateLoadingView() + ?: Sword.get().loadingViewFactory.createLoadingDelegate(requireContext()) + loadingView + ?: throw NullPointerException("you need to config LoadingViewFactory") } - if (loadingViewImpl == null) { - loadingView = Sword.get().loadingViewFactory.createLoadingDelegate(requireContext()) - } - return loadingViewImpl } protected open fun onCreateLoadingView(): LoadingView? { @@ -231,31 +232,31 @@ open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, Fragment } override fun showLoadingDialog() { - loadingView()?.showLoadingDialog(true) + loadingView().showLoadingDialog(true) } override fun showLoadingDialog(cancelable: Boolean) { - loadingView()?.showLoadingDialog(cancelable) + loadingView().showLoadingDialog(cancelable) } override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { - loadingView()?.showLoadingDialog(message, cancelable) + loadingView().showLoadingDialog(message, cancelable) } override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { - loadingView()?.showLoadingDialog(messageId, cancelable) + loadingView().showLoadingDialog(messageId, cancelable) } override fun dismissLoadingDialog() { - loadingView()?.dismissLoadingDialog() + loadingView().dismissLoadingDialog() } override fun showMessage(message: CharSequence) { - loadingView()?.showMessage(message) + loadingView().showMessage(message) } override fun showMessage(@StringRes messageId: Int) { - loadingView()?.showMessage(messageId) + loadingView().showMessage(messageId) } override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { diff --git a/lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt b/lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt index bdb42fa..27c0991 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt +++ b/lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt @@ -44,7 +44,7 @@ abstract class BaseStateDialogFragment : BaseDialogFragment(), RefreshStateLayou protected open fun onRetry(@StateLayoutConfig.RetryableState state: Int) { refreshView.ifNonNull { - if (this.isRefreshing) { + if (!this.isRefreshing) { autoRefresh() } } otherwise { diff --git a/lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt b/lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt index 7e26c84..e15c092 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt +++ b/lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt @@ -51,7 +51,7 @@ abstract class BaseStateFragment : BaseFragment(), RefreshStateLayout { protected open fun onRetry(@RetryableState state: Int) { refreshView.ifNonNull { - if (this.isRefreshing) { + if (!this.isRefreshing) { autoRefresh() } } otherwise { diff --git a/lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java b/lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java index 0e9c762..d373617 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java +++ b/lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java @@ -7,7 +7,6 @@ import android.view.animation.AnimationUtils; import com.android.base.R; import androidx.fragment.app.FragmentTransaction; -import timber.log.Timber; public final class FragmentAnimatorHelper { @@ -17,7 +16,6 @@ public final class FragmentAnimatorHelper { public FragmentAnimatorHelper(Context context, FragmentAnimator fragmentAnimator) { this.context = context; this.fragmentAnimator = fragmentAnimator; - Timber.d("context %s, animator %s", context.toString(), fragmentAnimator.toString()); } public void changeAnimation(FragmentAnimator fragmentAnimator) { diff --git a/lib_base/src/main/java/com/android/base/app/fragment/injectable/InjectableExtension.kt b/lib_base/src/main/java/com/android/base/app/fragment/injectable/InjectableExtension.kt index ac687cf..7e68a9d 100644 --- a/lib_base/src/main/java/com/android/base/app/fragment/injectable/InjectableExtension.kt +++ b/lib_base/src/main/java/com/android/base/app/fragment/injectable/InjectableExtension.kt @@ -50,19 +50,23 @@ fun H.handleLiveResourceWithData( force: Boolean = true, onSuccess: (T) -> Unit ) where H : InjectableExtension, H : LoadingView, H : LifecycleOwner { - liveData.observe(this, Observer { when { it.isError -> { + Timber.d("handleLiveResourceWithData -> isError") dismissLoadingDialog() errorHandler.handleError(it.error()) } it.isLoading -> { + Timber.d("handleLiveResourceWithData -> isLoading") showLoadingDialog(!force) } - it.isSuccess && it.hasData() -> { + it.isSuccess -> { + Timber.d("handleLiveResourceWithData -> isSuccess") dismissLoadingDialog() - onSuccess(it.data()) + if (it.hasData()) { + onSuccess(it.data()) + } } } }) diff --git a/lib_base/src/main/java/com/android/base/app/ui/UIEx.kt b/lib_base/src/main/java/com/android/base/app/ui/UIEx.kt index e9e4413..6b4e134 100644 --- a/lib_base/src/main/java/com/android/base/app/ui/UIEx.kt +++ b/lib_base/src/main/java/com/android/base/app/ui/UIEx.kt @@ -6,7 +6,7 @@ import com.android.base.app.Sword import com.android.base.utils.common.isEmpty import timber.log.Timber -fun RefreshListLayout.processListResultWithStatus(list: List?, onEmpty: (() -> Unit)? = null) { +fun RefreshListLayout.handleListResultWithStatus(list: List?, onEmpty: (() -> Unit)? = null) { if (isLoadingMore) { if (!isEmpty(list)) { addData(list) @@ -31,7 +31,7 @@ fun RefreshListLayout.processListResultWithStatus(list: List?, onEmpty } } -fun RefreshListLayout.processListResultWithoutStatus(list: List?, onEmpty: (() -> Unit)? = null) { +fun RefreshListLayout.handleListResultWithoutStatus(list: List?, onEmpty: (() -> Unit)? = null) { if (isLoadingMore) { if (!isEmpty(list)) { addData(list) @@ -83,7 +83,7 @@ fun RefreshListLayout.submitListResultWithoutStatus(list: List?, hasMo } } -fun RefreshListLayout<*>.processListErrorWithStatus(throwable: Throwable) { +fun RefreshListLayout<*>.handleListErrorWithStatus(throwable: Throwable) { if (isRefreshing) { refreshCompleted() } @@ -106,7 +106,7 @@ fun RefreshListLayout<*>.processListErrorWithStatus(throwable: Throwable) { } } -fun RefreshListLayout<*>.processListErrorWithoutStatus() { +fun RefreshListLayout<*>.handleListErrorWithoutStatus() { if (isRefreshing) { refreshCompleted() } @@ -125,7 +125,7 @@ fun RefreshListLayout<*>.showLoadingIfEmpty() { } } -fun RefreshStateLayout.processResultWithStatus(t: T?, onResult: ((T) -> Unit)) { +fun RefreshStateLayout.handleResultWithStatus(t: T?, onResult: ((T) -> Unit)) { if (isRefreshing) { refreshCompleted() } @@ -137,7 +137,7 @@ fun RefreshStateLayout.processResultWithStatus(t: T?, onResult: ((T) -> Unit } } -fun RefreshStateLayout.processErrorWithStatus(throwable: Throwable?) { +fun RefreshStateLayout.handleErrorWithStatus(throwable: Throwable?) { if (throwable == null) { Timber.d("processErrorWithStatus called, but throwable is null") return diff --git a/lib_base/src/main/java/com/android/base/utils/common/Times.kt b/lib_base/src/main/java/com/android/base/utils/common/Times.kt new file mode 100644 index 0000000..26d9eb9 --- /dev/null +++ b/lib_base/src/main/java/com/android/base/utils/common/Times.kt @@ -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) +} diff --git a/lib_base/src/main/java/com/android/base/widget/MultiStateView.java b/lib_base/src/main/java/com/android/base/widget/MultiStateView.java index ed25d9b..b5e3edd 100644 --- a/lib_base/src/main/java/com/android/base/widget/MultiStateView.java +++ b/lib_base/src/main/java/com/android/base/widget/MultiStateView.java @@ -11,12 +11,12 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import com.android.base.R; -import com.android.base.app.ui.StateLayoutConfig; import com.android.base.app.ui.StateLayoutConfig.ViewState; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import static com.android.base.app.ui.StateLayoutConfig.BLANK; import static com.android.base.app.ui.StateLayoutConfig.CONTENT; import static com.android.base.app.ui.StateLayoutConfig.EMPTY; import static com.android.base.app.ui.StateLayoutConfig.ERROR; @@ -83,13 +83,14 @@ public class MultiStateView extends FrameLayout { private void ensureInitState(int viewState) { /* - + + */ switch (viewState) { case 2: @@ -110,6 +111,9 @@ public class MultiStateView extends FrameLayout { case 7: mViewState = REQUESTING; break; + case 8: + mViewState = BLANK; + break; case 1: default: mViewState = CONTENT; @@ -194,7 +198,7 @@ public class MultiStateView extends FrameLayout { @Nullable @SuppressWarnings("unused") public View getView(@ViewState int state) { - if (state == StateLayoutConfig.BLANK) { + if (state == BLANK) { return null; } return ensureStateView(state); @@ -259,7 +263,7 @@ public class MultiStateView extends FrameLayout { * Shows the {@link View} based on the {@link ViewState} */ private void setView() { - if (mViewState == StateLayoutConfig.BLANK) { + if (mViewState == BLANK) { int size = mChildren.size(); View view; for (int i = 0; i < size; i++) { diff --git a/lib_base/src/main/res/values/base_attrs.xml b/lib_base/src/main/res/values/base_attrs.xml index fc60ef6..6990afd 100644 --- a/lib_base/src/main/res/values/base_attrs.xml +++ b/lib_base/src/main/res/values/base_attrs.xml @@ -41,6 +41,7 @@ + diff --git a/lib_coroutines/.gitignore b/lib_coroutines/.gitignore new file mode 100644 index 0000000..c06fb85 --- /dev/null +++ b/lib_coroutines/.gitignore @@ -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 \ No newline at end of file diff --git a/lib_coroutines/README.md b/lib_coroutines/README.md new file mode 100644 index 0000000..58ab902 --- /dev/null +++ b/lib_coroutines/README.md @@ -0,0 +1,3 @@ +# Explanation + +modifying from [kotlin-coroutines-android](https://github.com/enbandari/kotlin-coroutines-android) to support AndroidX. \ No newline at end of file diff --git a/lib_coroutines/build.gradle b/lib_coroutines/build.gradle new file mode 100644 index 0000000..1b33170 --- /dev/null +++ b/lib_coroutines/build.gradle @@ -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 +} \ No newline at end of file diff --git a/lib_coroutines/proguard-rules.pro b/lib_coroutines/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/lib_coroutines/proguard-rules.pro @@ -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 diff --git a/lib_coroutines/src/main/AndroidManifest.xml b/lib_coroutines/src/main/AndroidManifest.xml new file mode 100644 index 0000000..c8f6b8d --- /dev/null +++ b/lib_coroutines/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJob.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJob.kt new file mode 100644 index 0000000..7d13fc1 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJob.kt @@ -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) diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJobView.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJobView.kt new file mode 100644 index 0000000..6cd952c --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJobView.kt @@ -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) + } +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/MainScope.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/MainScope.kt new file mode 100644 index 0000000..a76b8e5 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/MainScope.kt @@ -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) +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/exception/Exceptions.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/exception/Exceptions.kt new file mode 100644 index 0000000..87c6d0c --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/exception/Exceptions.kt @@ -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") \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/ActivityLifecycleCallbackImpl.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/ActivityLifecycleCallbackImpl.kt new file mode 100644 index 0000000..54e0368 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/ActivityLifecycleCallbackImpl.kt @@ -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() + } + } + } + } + +} diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/FragmentLifecycleCallbackImpl.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/FragmentLifecycleCallbackImpl.kt new file mode 100644 index 0000000..8aaa2b5 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/FragmentLifecycleCallbackImpl.kt @@ -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() + } + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/Builders.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/Builders.kt new file mode 100644 index 0000000..2fa809b --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/Builders.kt @@ -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 +} diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyDisposableHandle.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyDisposableHandle.kt new file mode 100644 index 0000000..a000151 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyDisposableHandle.kt @@ -0,0 +1,7 @@ +package com.bennyhuo.kotlin.coroutines.android.mainscope.job + +import kotlinx.coroutines.DisposableHandle + +object EmptyDisposableHandle: DisposableHandle { + override fun dispose() = Unit +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyInterceptor.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyInterceptor.kt new file mode 100644 index 0000000..ec4db9d --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyInterceptor.kt @@ -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 interceptContinuation(continuation: Continuation): Continuation { + return EmptyContinuation as Continuation + } + + private object EmptyContinuation: Continuation{ + override val context: CoroutineContext = EmptyCoroutineContext + + override fun resumeWith(result: Result) { + Logcat.warn("Intercepted coroutine. won't resume.") + } + } + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyJob.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyJob.kt new file mode 100644 index 0000000..14f13fa --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyJob.kt @@ -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 = 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 registerSelectClause0(select: SelectInstance, 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 get(key: Key): E? = + if (this.key == key) this as E else null + + override fun 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 + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/ImmutableCoroutineContext.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/ImmutableCoroutineContext.kt new file mode 100644 index 0000000..103460f --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/ImmutableCoroutineContext.kt @@ -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 fold(initial: R, operation: (R, Element) -> R): R = coroutineContext.fold(initial, operation) + + override fun get(key: Key) = coroutineContext[key] + + override fun minusKey(key: Key<*>) = this + + override fun plus(context: CoroutineContext) = this +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/JobCompat.java b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/JobCompat.java new file mode 100644 index 0000000..8e86b94 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/JobCompat.java @@ -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); + } +} diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/StandaloneCoroutineCompat.java b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/StandaloneCoroutineCompat.java new file mode 100644 index 0000000..233566c --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/StandaloneCoroutineCompat.java @@ -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 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; + } +} diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/AppCompatScoped.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/AppCompatScoped.kt new file mode 100644 index 0000000..dad8bff --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/AppCompatScoped.kt @@ -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) + } + } + } + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/BasicScoped.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/BasicScoped.kt new file mode 100644 index 0000000..f274729 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/BasicScoped.kt @@ -0,0 +1,1066 @@ +package com.bennyhuo.kotlin.coroutines.android.mainscope.scope + +import android.view.WindowInsets +import com.bennyhuo.kotlin.coroutines.android.mainscope.MainScope +import com.bennyhuo.kotlin.coroutines.android.mainscope.job.launch + +interface BasicScoped: MainScoped { + + fun android.view.View.onLayoutChange( + handler: suspend MainScope.(v: android.view.View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) -> Unit + ) { + addOnLayoutChangeListener { v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom -> + mainScope.launch { + handler(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) + } + } + } + + fun android.view.View.onAttachStateChangeListener( + init: __View_OnAttachStateChangeListener.() -> Unit + ) { + val listener = __View_OnAttachStateChangeListener(mainScope) + listener.init() + addOnAttachStateChangeListener(listener) + } + + class __View_OnAttachStateChangeListener(private val scope: MainScope) : android.view.View.OnAttachStateChangeListener { + + private var _onViewAttachedToWindow: (suspend MainScope.(android.view.View) -> Unit)? = null + + override fun onViewAttachedToWindow(v: android.view.View) { + val handler = _onViewAttachedToWindow ?: return + scope.launch { + handler(v) + } + } + + fun onViewAttachedToWindow( + listener: suspend MainScope.(android.view.View) -> Unit + ) { + _onViewAttachedToWindow = listener + } + + private var _onViewDetachedFromWindow: (suspend MainScope.(android.view.View) -> Unit)? = null + + override fun onViewDetachedFromWindow(v: android.view.View) { + val handler = _onViewDetachedFromWindow ?: return + scope.launch { + handler(v) + } + } + + fun onViewDetachedFromWindow( + listener: suspend MainScope.(android.view.View) -> Unit + ) { + _onViewDetachedFromWindow = listener + } + + }fun android.view.View.onUnhandledKeyEvent( + returnValue: Boolean = false, + handler: suspend MainScope.(p0: android.view.View?, p1: android.view.KeyEvent?) -> Unit + ) { + addOnUnhandledKeyEventListener { p0, p1 -> + mainScope.launch { + handler(p0, p1) + } + returnValue + } + } + + fun android.widget.TextView.textChangedListener( + init: __TextWatcher.() -> Unit + ) { + val listener = __TextWatcher(mainScope) + listener.init() + addTextChangedListener(listener) + } + + class __TextWatcher(private val scope: MainScope) : android.text.TextWatcher { + + private var _beforeTextChanged: (suspend MainScope.(CharSequence?, Int, Int, Int) -> Unit)? = null + + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + val handler = _beforeTextChanged ?: return + scope.launch { + handler(s, start, count, after) + } + } + + fun beforeTextChanged( + listener: suspend MainScope.(CharSequence?, Int, Int, Int) -> Unit + ) { + _beforeTextChanged = listener + } + + private var _onTextChanged: (suspend MainScope.(CharSequence?, Int, Int, Int) -> Unit)? = null + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + val handler = _onTextChanged ?: return + scope.launch { + handler(s, start, before, count) + } + } + + fun onTextChanged( + listener: suspend MainScope.(CharSequence?, Int, Int, Int) -> Unit + ) { + _onTextChanged = listener + } + + private var _afterTextChanged: (suspend MainScope.(android.text.Editable?) -> Unit)? = null + + override fun afterTextChanged(s: android.text.Editable?) { + val handler = _afterTextChanged ?: return + scope.launch { + handler(s) + } + } + + fun afterTextChanged( + listener: suspend MainScope.(android.text.Editable?) -> Unit + ) { + _afterTextChanged = listener + } + + }fun android.gesture.GestureOverlayView.onGestureListener( + init: __GestureOverlayView_OnGestureListener.() -> Unit + ) { + val listener = __GestureOverlayView_OnGestureListener(mainScope) + listener.init() + addOnGestureListener(listener) + } + + class __GestureOverlayView_OnGestureListener(private val scope: MainScope) : android.gesture.GestureOverlayView.OnGestureListener { + + private var _onGestureStarted: (suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit)? = null + + override fun onGestureStarted(overlay: android.gesture.GestureOverlayView?, event: android.view.MotionEvent?) { + val handler = _onGestureStarted ?: return + scope.launch { + handler(overlay, event) + } + } + + fun onGestureStarted( + listener: suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit + ) { + _onGestureStarted = listener + } + + private var _onGesture: (suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit)? = null + + override fun onGesture(overlay: android.gesture.GestureOverlayView?, event: android.view.MotionEvent?) { + val handler = _onGesture ?: return + scope.launch { + handler(overlay, event) + } + } + + fun onGesture( + listener: suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit + ) { + _onGesture = listener + } + + private var _onGestureEnded: (suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit)? = null + + override fun onGestureEnded(overlay: android.gesture.GestureOverlayView?, event: android.view.MotionEvent?) { + val handler = _onGestureEnded ?: return + scope.launch { + handler(overlay, event) + } + } + + fun onGestureEnded( + listener: suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit + ) { + _onGestureEnded = listener + } + + private var _onGestureCancelled: (suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit)? = null + + override fun onGestureCancelled(overlay: android.gesture.GestureOverlayView?, event: android.view.MotionEvent?) { + val handler = _onGestureCancelled ?: return + scope.launch { + handler(overlay, event) + } + } + + fun onGestureCancelled( + listener: suspend MainScope.(android.gesture.GestureOverlayView?, android.view.MotionEvent?) -> Unit + ) { + _onGestureCancelled = listener + } + + }fun android.gesture.GestureOverlayView.onGesturePerformed( + handler: suspend MainScope.(overlay: android.gesture.GestureOverlayView?, gesture: android.gesture.Gesture?) -> Unit + ) { + addOnGesturePerformedListener { overlay, gesture -> + mainScope.launch { + handler(overlay, gesture) + } + } + } + + fun android.gesture.GestureOverlayView.onGesturingListener( + init: __GestureOverlayView_OnGesturingListener.() -> Unit + ) { + val listener = __GestureOverlayView_OnGesturingListener(mainScope) + listener.init() + addOnGesturingListener(listener) + } + + class __GestureOverlayView_OnGesturingListener(private val scope: MainScope) : android.gesture.GestureOverlayView.OnGesturingListener { + + private var _onGesturingStarted: (suspend MainScope.(android.gesture.GestureOverlayView?) -> Unit)? = null + + override fun onGesturingStarted(overlay: android.gesture.GestureOverlayView?) { + val handler = _onGesturingStarted ?: return + scope.launch { + handler(overlay) + } + } + + fun onGesturingStarted( + listener: suspend MainScope.(android.gesture.GestureOverlayView?) -> Unit + ) { + _onGesturingStarted = listener + } + + private var _onGesturingEnded: (suspend MainScope.(android.gesture.GestureOverlayView?) -> Unit)? = null + + override fun onGesturingEnded(overlay: android.gesture.GestureOverlayView?) { + val handler = _onGesturingEnded ?: return + scope.launch { + handler(overlay) + } + } + + fun onGesturingEnded( + listener: suspend MainScope.(android.gesture.GestureOverlayView?) -> Unit + ) { + _onGesturingEnded = listener + } + + }fun android.media.tv.TvView.onUnhandledInputEvent( + returnValue: Boolean = false, + handler: suspend MainScope.(event: android.view.InputEvent?) -> Unit + ) { + setOnUnhandledInputEventListener { event -> + mainScope.launch { + handler(event) + } + returnValue + } + } + + fun android.view.View.onApplyWindowInsets( + returnValue: WindowInsets, + handler: suspend MainScope.(v: android.view.View?, insets: android.view.WindowInsets?) -> Unit + ) { + setOnApplyWindowInsetsListener { v, insets -> + mainScope.launch { + handler(v, insets) + } + returnValue + } + } + + fun android.view.View.onCapturedPointer( + returnValue: Boolean = false, + handler: suspend MainScope.(view: android.view.View?, event: android.view.MotionEvent?) -> Unit + ) { + setOnCapturedPointerListener { view, event -> + mainScope.launch { + handler(view, event) + } + returnValue + } + } + + fun android.view.View.onClick( + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnClickListener { v -> + mainScope.launch { + handler(v) + } + } + } + + fun android.view.View.onContextClick( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnContextClickListener { v -> + mainScope.launch { + handler(v) + } + returnValue + } + } + + fun android.view.View.onCreateContextMenu( + handler: suspend MainScope.(menu: android.view.ContextMenu?, v: android.view.View?, menuInfo: android.view.ContextMenu.ContextMenuInfo?) -> Unit + ) { + setOnCreateContextMenuListener { menu, v, menuInfo -> + mainScope.launch { + handler(menu, v, menuInfo) + } + } + } + + fun android.view.View.onDrag( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View, event: android.view.DragEvent) -> Unit + ) { + setOnDragListener { v, event -> + mainScope.launch { + handler(v, event) + } + returnValue + } + } + + fun android.view.View.onFocusChange( + handler: suspend MainScope.(v: android.view.View, hasFocus: Boolean) -> Unit + ) { + setOnFocusChangeListener { v, hasFocus -> + mainScope.launch { + handler(v, hasFocus) + } + } + } + + fun android.view.View.onGenericMotion( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View, event: android.view.MotionEvent) -> Unit + ) { + setOnGenericMotionListener { v, event -> + mainScope.launch { + handler(v, event) + } + returnValue + } + } + + fun android.view.View.onHover( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View, event: android.view.MotionEvent) -> Unit + ) { + setOnHoverListener { v, event -> + mainScope.launch { + handler(v, event) + } + returnValue + } + } + + fun android.view.View.onKey( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View, keyCode: Int, event: android.view.KeyEvent?) -> Unit + ) { + setOnKeyListener { v, keyCode, event -> + mainScope.launch { + handler(v, keyCode, event) + } + returnValue + } + } + + fun android.view.View.onLongClick( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnLongClickListener { v -> + mainScope.launch { + handler(v) + } + returnValue + } + } + + fun android.view.View.onScrollChange( + handler: suspend MainScope.(v: android.view.View?, scrollX: Int, scrollY: Int, oldScrollX: Int, oldScrollY: Int) -> Unit + ) { + setOnScrollChangeListener { v, scrollX, scrollY, oldScrollX, oldScrollY -> + mainScope.launch { + handler(v, scrollX, scrollY, oldScrollX, oldScrollY) + } + } + } + + fun android.view.View.onSystemUiVisibilityChange( + handler: suspend MainScope.(visibility: Int) -> Unit + ) { + setOnSystemUiVisibilityChangeListener { visibility -> + mainScope.launch { + handler(visibility) + } + } + } + + fun android.view.View.onTouch( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.view.View, event: android.view.MotionEvent) -> Unit + ) { + setOnTouchListener { v, event -> + mainScope.launch { + handler(v, event) + } + returnValue + } + } + + fun android.view.ViewGroup.onHierarchyChangeListener( + init: __ViewGroup_OnHierarchyChangeListener.() -> Unit + ) { + val listener = __ViewGroup_OnHierarchyChangeListener(mainScope) + listener.init() + setOnHierarchyChangeListener(listener) + } + + class __ViewGroup_OnHierarchyChangeListener(private val scope: MainScope) : android.view.ViewGroup.OnHierarchyChangeListener { + + private var _onChildViewAdded: (suspend MainScope.(android.view.View?, android.view.View?) -> Unit)? = null + + override fun onChildViewAdded(parent: android.view.View?, child: android.view.View?) { + val handler = _onChildViewAdded ?: return + scope.launch { + handler(parent, child) + } + } + + fun onChildViewAdded( + listener: suspend MainScope.(android.view.View?, android.view.View?) -> Unit + ) { + _onChildViewAdded = listener + } + + private var _onChildViewRemoved: (suspend MainScope.(android.view.View?, android.view.View?) -> Unit)? = null + + + override fun onChildViewRemoved(parent: android.view.View?, child: android.view.View?) { + val handler = _onChildViewRemoved ?: return + scope.launch { + handler(parent, child) + } + } + + fun onChildViewRemoved( + listener: suspend MainScope.(android.view.View?, android.view.View?) -> Unit + ) { + _onChildViewRemoved = listener + } + + }fun android.view.ViewStub.onInflate( + handler: suspend MainScope.(stub: android.view.ViewStub?, inflated: android.view.View?) -> Unit + ) { + setOnInflateListener { stub, inflated -> + mainScope.launch { + handler(stub, inflated) + } + } + } + + fun android.widget.AbsListView.onScrollListener( + init: __AbsListView_OnScrollListener.() -> Unit + ) { + val listener = __AbsListView_OnScrollListener(mainScope) + listener.init() + setOnScrollListener(listener) + } + + class __AbsListView_OnScrollListener(private val scope: MainScope) : android.widget.AbsListView.OnScrollListener { + + private var _onScrollStateChanged: (suspend MainScope.(android.widget.AbsListView?, Int) -> Unit)? = null + + override fun onScrollStateChanged(view: android.widget.AbsListView?, scrollState: Int) { + val handler = _onScrollStateChanged ?: return + scope.launch { + handler(view, scrollState) + } + } + + fun onScrollStateChanged( + listener: suspend MainScope.(android.widget.AbsListView?, Int) -> Unit + ) { + _onScrollStateChanged = listener + } + + private var _onScroll: (suspend MainScope.(android.widget.AbsListView?, Int, Int, Int) -> Unit)? = null + + + override fun onScroll(view: android.widget.AbsListView?, firstVisibleItem: Int, visibleItemCount: Int, totalItemCount: Int) { + val handler = _onScroll ?: return + scope.launch { + handler(view, firstVisibleItem, visibleItemCount, totalItemCount) + } + } + + fun onScroll( + listener: suspend MainScope.(android.widget.AbsListView?, Int, Int, Int) -> Unit + ) { + _onScroll = listener + } + + }fun android.widget.ActionMenuView.onMenuItemClick( + returnValue: Boolean = false, + handler: suspend MainScope.(item: android.view.MenuItem?) -> Unit + ) { + setOnMenuItemClickListener { item -> + mainScope.launch { + handler(item) + } + returnValue + } + } + + fun android.widget.AdapterView.onItemClick( + handler: suspend MainScope.(p0: android.widget.AdapterView<*>?, p1: android.view.View?, p2: Int, p3: Long) -> Unit + ) { + setOnItemClickListener { p0, p1, p2, p3 -> + mainScope.launch { + handler(p0, p1, p2, p3) + } + } + } + + fun android.widget.AdapterView.onItemLongClick( + returnValue: Boolean = false, + handler: suspend MainScope.(p0: android.widget.AdapterView<*>?, p1: android.view.View?, p2: Int, p3: Long) -> Unit + ) { + setOnItemLongClickListener { p0, p1, p2, p3 -> + mainScope.launch { + handler(p0, p1, p2, p3) + } + returnValue + } + } + + fun android.widget.AdapterView.onItemSelectedListener( + init: __AdapterView_OnItemSelectedListener.() -> Unit + ) { + val listener = __AdapterView_OnItemSelectedListener(mainScope) + listener.init() + setOnItemSelectedListener(listener) + } + + class __AdapterView_OnItemSelectedListener(private val scope: MainScope) : android.widget.AdapterView.OnItemSelectedListener { + + private var _onItemSelected: (suspend MainScope.(android.widget.AdapterView<*>?, android.view.View?, Int, Long) -> Unit)? = null + + + override fun onItemSelected(p0: android.widget.AdapterView<*>?, p1: android.view.View?, p2: Int, p3: Long) { + val handler = _onItemSelected ?: return + scope.launch { + handler(p0, p1, p2, p3) + } + } + + fun onItemSelected( + listener: suspend MainScope.(android.widget.AdapterView<*>?, android.view.View?, Int, Long) -> Unit + ) { + _onItemSelected = listener + } + + private var _onNothingSelected: (suspend MainScope.(android.widget.AdapterView<*>?) -> Unit)? = null + + + override fun onNothingSelected(p0: android.widget.AdapterView<*>?) { + val handler = _onNothingSelected ?: return + scope.launch { + handler(p0) + } + } + + fun onNothingSelected( + listener: suspend MainScope.(android.widget.AdapterView<*>?) -> Unit + ) { + _onNothingSelected = listener + } + + }fun android.widget.AutoCompleteTextView.onDismiss( + handler: suspend MainScope.() -> Unit + ) { + setOnDismissListener { -> + mainScope.launch(block = handler) + } + } + + fun android.widget.CalendarView.onDateChange( + handler: suspend MainScope.(view: android.widget.CalendarView?, year: Int, month: Int, dayOfMonth: Int) -> Unit + ) { + setOnDateChangeListener { view, year, month, dayOfMonth -> + mainScope.launch { + handler(view, year, month, dayOfMonth) + } + } + } + + fun android.widget.Chronometer.onChronometerTick( + handler: suspend MainScope.(chronometer: android.widget.Chronometer?) -> Unit + ) { + setOnChronometerTickListener { chronometer -> + mainScope.launch { + handler(chronometer) + } + } + } + + fun android.widget.CompoundButton.onCheckedChange( + handler: suspend MainScope.(buttonView: android.widget.CompoundButton?, isChecked: Boolean) -> Unit + ) { + setOnCheckedChangeListener { buttonView, isChecked -> + mainScope.launch { + handler(buttonView, isChecked) + } + } + } + + fun android.widget.DatePicker.onDateChanged( + handler: suspend MainScope.(view: android.widget.DatePicker?, year: Int, monthOfYear: Int, dayOfMonth: Int) -> Unit + ) { + setOnDateChangedListener { view, year, monthOfYear, dayOfMonth -> + mainScope.launch { + handler(view, year, monthOfYear, dayOfMonth) + } + } + } + + fun android.widget.ExpandableListView.onChildClick( + returnValue: Boolean = false, + handler: suspend MainScope.(parent: android.widget.ExpandableListView?, v: android.view.View?, groupPosition: Int, childPosition: Int, id: Long) -> Unit + ) { + setOnChildClickListener { parent, v, groupPosition, childPosition, id -> + mainScope.launch { + handler(parent, v, groupPosition, childPosition, id) + } + returnValue + } + } + + fun android.widget.ExpandableListView.onGroupClick( + returnValue: Boolean = false, + handler: suspend MainScope.(parent: android.widget.ExpandableListView?, v: android.view.View?, groupPosition: Int, id: Long) -> Unit + ) { + setOnGroupClickListener { parent, v, groupPosition, id -> + mainScope.launch { + handler(parent, v, groupPosition, id) + } + returnValue + } + } + + fun android.widget.ExpandableListView.onGroupCollapse( + handler: suspend MainScope.(groupPosition: Int) -> Unit + ) { + setOnGroupCollapseListener { groupPosition -> + mainScope.launch { + handler(groupPosition) + } + } + } + + fun android.widget.ExpandableListView.onGroupExpand( + handler: suspend MainScope.(groupPosition: Int) -> Unit + ) { + setOnGroupExpandListener { groupPosition -> + mainScope.launch { + handler(groupPosition) + } + } + } + + fun android.widget.NumberPicker.onScroll( + handler: suspend MainScope.(view: android.widget.NumberPicker?, scrollState: Int) -> Unit + ) { + setOnScrollListener { view, scrollState -> + mainScope.launch { + handler(view, scrollState) + } + } + } + + fun android.widget.NumberPicker.onValueChanged( + handler: suspend MainScope.(picker: android.widget.NumberPicker?, oldVal: Int, newVal: Int) -> Unit + ) { + setOnValueChangedListener { picker, oldVal, newVal -> + mainScope.launch { + handler(picker, oldVal, newVal) + } + } + } + + fun android.widget.RadioGroup.onCheckedChange( + handler: suspend MainScope.(group: android.widget.RadioGroup?, checkedId: Int) -> Unit + ) { + setOnCheckedChangeListener { group, checkedId -> + mainScope.launch { + handler(group, checkedId) + } + } + } + + fun android.widget.RatingBar.onRatingBarChange( + handler: suspend MainScope.(ratingBar: android.widget.RatingBar?, rating: Float, fromUser: Boolean) -> Unit + ) { + setOnRatingBarChangeListener { ratingBar, rating, fromUser -> + mainScope.launch { + handler(ratingBar, rating, fromUser) + } + } + } + + fun android.widget.SearchView.onClose( + returnValue: Boolean = false, + handler: suspend MainScope.() -> Unit + ) { + setOnCloseListener { -> + mainScope.launch(block = handler) + returnValue + } + } + + fun android.widget.SearchView.onQueryTextFocusChange( + handler: suspend MainScope.(v: android.view.View, hasFocus: Boolean) -> Unit + ) { + setOnQueryTextFocusChangeListener { v, hasFocus -> + mainScope.launch { + handler(v, hasFocus) + } + } + } + + fun android.widget.SearchView.onQueryTextListener( + init: __SearchView_OnQueryTextListener.() -> Unit + ) { + val listener = __SearchView_OnQueryTextListener(mainScope) + listener.init() + setOnQueryTextListener(listener) + } + + class __SearchView_OnQueryTextListener(private val scope: MainScope) : android.widget.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 + scope.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 + scope.launch { + handler(newText) + } + return returnValue + } + + fun onQueryTextChange( + returnValue: Boolean = false, + listener: suspend MainScope.(String?) -> Boolean + ) { + _onQueryTextChange = listener + _onQueryTextChange_returnValue = returnValue + } + + }fun android.widget.SearchView.onSearchClick( + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnSearchClickListener { v -> + mainScope.launch { + handler(v) + } + } + } + + fun android.widget.SearchView.onSuggestionListener( + init: __SearchView_OnSuggestionListener.() -> Unit + ) { + val listener = __SearchView_OnSuggestionListener(mainScope) + listener.init() + setOnSuggestionListener(listener) + } + + class __SearchView_OnSuggestionListener(private val scope: MainScope) : android.widget.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 + scope.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 + scope.launch { + handler(position) + } + return returnValue + } + + fun onSuggestionClick( + returnValue: Boolean = false, + listener: suspend MainScope.(Int) -> Boolean + ) { + _onSuggestionClick = listener + _onSuggestionClick_returnValue = returnValue + } + + }fun android.widget.SeekBar.onSeekBarChangeListener( + init: __SeekBar_OnSeekBarChangeListener.() -> Unit + ) { + val listener = __SeekBar_OnSeekBarChangeListener(mainScope) + listener.init() + setOnSeekBarChangeListener(listener) + } + + class __SeekBar_OnSeekBarChangeListener(private val scope: MainScope) : android.widget.SeekBar.OnSeekBarChangeListener { + + private var _onProgressChanged: (suspend MainScope.(android.widget.SeekBar?, Int, Boolean) -> Unit)? = null + + + override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) { + val handler = _onProgressChanged ?: return + scope.launch { + handler(seekBar, progress, fromUser) + } + } + + fun onProgressChanged( + listener: suspend MainScope.(android.widget.SeekBar?, Int, Boolean) -> Unit + ) { + _onProgressChanged = listener + } + + private var _onStartTrackingTouch: (suspend MainScope.(android.widget.SeekBar?) -> Unit)? = null + + + override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) { + val handler = _onStartTrackingTouch ?: return + scope.launch { + handler(seekBar) + } + } + + fun onStartTrackingTouch( + listener: suspend MainScope.(android.widget.SeekBar?) -> Unit + ) { + _onStartTrackingTouch = listener + } + + private var _onStopTrackingTouch: (suspend MainScope.(android.widget.SeekBar?) -> Unit)? = null + + + override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) { + val handler = _onStopTrackingTouch ?: return + scope.launch { + handler(seekBar) + } + } + + fun onStopTrackingTouch( + listener: suspend MainScope.(android.widget.SeekBar?) -> Unit + ) { + _onStopTrackingTouch = listener + } + + }fun android.widget.SlidingDrawer.onDrawerClose( + handler: suspend MainScope.() -> Unit + ) { + setOnDrawerCloseListener { -> + mainScope.launch(block = handler) + } + } + + fun android.widget.SlidingDrawer.onDrawerOpen( + handler: suspend MainScope.() -> Unit + ) { + setOnDrawerOpenListener { -> + mainScope.launch(block = handler) + } + } + + fun android.widget.SlidingDrawer.onDrawerScrollListener( + init: __SlidingDrawer_OnDrawerScrollListener.() -> Unit + ) { + val listener = __SlidingDrawer_OnDrawerScrollListener(mainScope) + listener.init() + setOnDrawerScrollListener(listener) + } + + class __SlidingDrawer_OnDrawerScrollListener(private val scope: MainScope) : android.widget.SlidingDrawer.OnDrawerScrollListener { + + private var _onScrollStarted: (suspend MainScope.() -> Unit)? = null + + + override fun onScrollStarted() { + val handler = _onScrollStarted ?: return + scope.launch(block = handler) + } + + fun onScrollStarted( + listener: suspend MainScope.() -> Unit + ) { + _onScrollStarted = listener + } + + private var _onScrollEnded: (suspend MainScope.() -> Unit)? = null + + + override fun onScrollEnded() { + val handler = _onScrollEnded ?: return + scope.launch(block = handler) + } + + fun onScrollEnded( + listener: suspend MainScope.() -> Unit + ) { + _onScrollEnded = listener + } + + }fun android.widget.TabHost.onTabChanged( + handler: suspend MainScope.(tabId: String?) -> Unit + ) { + setOnTabChangedListener { tabId -> + mainScope.launch { + handler(tabId) + } + } + } + + fun android.widget.TextView.onEditorAction( + returnValue: Boolean = false, + handler: suspend MainScope.(v: android.widget.TextView?, actionId: Int, event: android.view.KeyEvent?) -> Unit + ) { + setOnEditorActionListener { v, actionId, event -> + mainScope.launch { + handler(v, actionId, event) + } + returnValue + } + } + + fun android.widget.TimePicker.onTimeChanged( + handler: suspend MainScope.(view: android.widget.TimePicker?, hourOfDay: Int, minute: Int) -> Unit + ) { + setOnTimeChangedListener { view, hourOfDay, minute -> + mainScope.launch { + handler(view, hourOfDay, minute) + } + } + } + + fun android.widget.Toolbar.onMenuItemClick( + returnValue: Boolean = false, + handler: suspend MainScope.(item: android.view.MenuItem?) -> Unit + ) { + setOnMenuItemClickListener { item -> + mainScope.launch { + handler(item) + } + returnValue + } + } + + fun android.widget.VideoView.onCompletion( + handler: suspend MainScope.(mp: android.media.MediaPlayer?) -> Unit + ) { + setOnCompletionListener { mp -> + mainScope.launch { + handler(mp) + } + } + } + + fun android.widget.VideoView.onError( + returnValue: Boolean = false, + handler: suspend MainScope.(mp: android.media.MediaPlayer?, what: Int, extra: Int) -> Unit + ) { + setOnErrorListener { mp, what, extra -> + mainScope.launch { + handler(mp, what, extra) + } + returnValue + } + } + + fun android.widget.VideoView.onInfo( + returnValue: Boolean = false, + handler: suspend MainScope.(mp: android.media.MediaPlayer?, what: Int, extra: Int) -> Unit + ) { + setOnInfoListener { mp, what, extra -> + mainScope.launch { + handler(mp, what, extra) + } + returnValue + } + } + + fun android.widget.VideoView.onPrepared( + handler: suspend MainScope.(mp: android.media.MediaPlayer?) -> Unit + ) { + setOnPreparedListener { mp -> + mainScope.launch { + handler(mp) + } + } + } + + fun android.widget.ZoomControls.onZoomInClick( + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnZoomInClickListener { v -> + mainScope.launch { + handler(v) + } + } + } + + fun android.widget.ZoomControls.onZoomOutClick( + handler: suspend MainScope.(v: android.view.View?) -> Unit + ) { + setOnZoomOutClickListener { v -> + mainScope.launch { + handler(v) + } + } + } + +} diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/DesignScoped.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/DesignScoped.kt new file mode 100644 index 0000000..38c4891 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/DesignScoped.kt @@ -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 TabLayout.onTabSelectedListener( + init: __onTabSelected_TabLayout_BaseOnTabSelectedListener.() -> Unit + ) { + val listener = __onTabSelected_TabLayout_BaseOnTabSelectedListener(mainScope) + listener.init() + addOnTabSelectedListener(listener) + } + + class __onTabSelected_TabLayout_BaseOnTabSelectedListener(private val mainScope: MainScope) : TabLayout.BaseOnTabSelectedListener { + + 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 + } + } + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/MainScoped.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/MainScoped.kt new file mode 100644 index 0000000..dd7a96f --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/MainScoped.kt @@ -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() + } + + 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 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() +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/RecyclerViewScoped.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/RecyclerViewScoped.kt new file mode 100644 index 0000000..e434e06 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/RecyclerViewScoped.kt @@ -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 + } + + } + +} \ No newline at end of file diff --git a/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/utils/Logger.kt b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/utils/Logger.kt new file mode 100644 index 0000000..8d0e472 --- /dev/null +++ b/lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/utils/Logger.kt @@ -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() + } + } + +} \ No newline at end of file