fix bugs, add coroutines

androidx
Ztiany 5 years ago
parent 6448dfbf2c
commit 9dfe9842e2
  1. 2
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelFactory.java
  2. 2
      lib_base/src/main/java/com/android/base/app/dagger/ViewModelModule.java
  3. 27
      lib_base/src/main/java/com/android/base/app/fragment/BaseDialogFragment.kt
  4. 27
      lib_base/src/main/java/com/android/base/app/fragment/BaseFragment.kt
  5. 2
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateDialogFragment.kt
  6. 2
      lib_base/src/main/java/com/android/base/app/fragment/BaseStateFragment.kt
  7. 2
      lib_base/src/main/java/com/android/base/app/fragment/animator/FragmentAnimatorHelper.java
  8. 8
      lib_base/src/main/java/com/android/base/app/fragment/injectable/InjectableExtension.kt
  9. 12
      lib_base/src/main/java/com/android/base/app/ui/UIEx.kt
  10. 11
      lib_base/src/main/java/com/android/base/utils/common/Times.kt
  11. 10
      lib_base/src/main/java/com/android/base/widget/MultiStateView.java
  12. 1
      lib_base/src/main/res/values/base_attrs.xml
  13. 22
      lib_coroutines/.gitignore
  14. 3
      lib_coroutines/README.md
  15. 41
      lib_coroutines/build.gradle
  16. 21
      lib_coroutines/proguard-rules.pro
  17. 2
      lib_coroutines/src/main/AndroidManifest.xml
  18. 38
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJob.kt
  19. 16
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/autodisposable/AutoDisposableJobView.kt
  20. 50
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/MainScope.kt
  21. 5
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/exception/Exceptions.kt
  22. 46
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/ActivityLifecycleCallbackImpl.kt
  23. 24
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/internal/FragmentLifecycleCallbackImpl.kt
  24. 21
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/Builders.kt
  25. 7
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyDisposableHandle.kt
  26. 20
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyInterceptor.kt
  27. 63
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/EmptyJob.kt
  28. 15
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/ImmutableCoroutineContext.kt
  29. 37
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/JobCompat.java
  30. 22
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/job/StandaloneCoroutineCompat.java
  31. 195
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/AppCompatScoped.kt
  32. 1066
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/BasicScoped.kt
  33. 369
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/DesignScoped.kt
  34. 73
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/MainScoped.kt
  35. 115
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/scope/RecyclerViewScoped.kt
  36. 22
      lib_coroutines/src/main/java/com/bennyhuo/kotlin/coroutines/android/mainscope/utils/Logger.kt

@ -11,6 +11,8 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider; import androidx.lifecycle.ViewModelProvider;
/** /**
* {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported.
*
* @author Ztiany * @author Ztiany
* Email: ztiany3@gmail.com * Email: ztiany3@gmail.com
* Date : 2018-11-05 14:31 * Date : 2018-11-05 14:31

@ -5,7 +5,7 @@ import dagger.Binds;
import dagger.Module; import dagger.Module;
/** /**
* 使用 ViewModelModule 情况下所有的 ViewModule 都由 Activity 界别容器提供因此 Fragment 级容器无法为其 ViewModule 提供依赖这是仅有的局限性 * {@link androidx.lifecycle.AbstractSavedStateViewModelFactory} is not supported.
* *
* @author Ztiany * @author Ztiany
* Email: ztiany3@gmail.com * Email: ztiany3@gmail.com

@ -204,15 +204,16 @@ open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPr
return false return false
} }
private fun loadingView(): LoadingView? { private fun loadingView(): LoadingView {
val loadingViewImpl = loadingView val loadingViewImpl = loadingView
if (loadingViewImpl == null) { return if (loadingViewImpl != null) {
loadingViewImpl
} else {
loadingView = onCreateLoadingView() 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? { protected open fun onCreateLoadingView(): LoadingView? {
@ -220,31 +221,31 @@ open class BaseDialogFragment : AppCompatDialogFragment(), LoadingView, OnBackPr
} }
override fun showLoadingDialog() { override fun showLoadingDialog() {
loadingView()?.showLoadingDialog(true) loadingView().showLoadingDialog(true)
} }
override fun showLoadingDialog(cancelable: Boolean) { override fun showLoadingDialog(cancelable: Boolean) {
loadingView()?.showLoadingDialog(cancelable) loadingView().showLoadingDialog(cancelable)
} }
override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) {
loadingView()?.showLoadingDialog(message, cancelable) loadingView().showLoadingDialog(message, cancelable)
} }
override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) {
loadingView()?.showLoadingDialog(messageId, cancelable) loadingView().showLoadingDialog(messageId, cancelable)
} }
override fun dismissLoadingDialog() { override fun dismissLoadingDialog() {
loadingView()?.dismissLoadingDialog() loadingView().dismissLoadingDialog()
} }
override fun showMessage(message: CharSequence) { override fun showMessage(message: CharSequence) {
loadingView()?.showMessage(message) loadingView().showMessage(message)
} }
override fun showMessage(@StringRes messageId: Int) { override fun showMessage(@StringRes messageId: Int) {
loadingView()?.showMessage(messageId) loadingView().showMessage(messageId)
} }
} }

@ -215,15 +215,16 @@ open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, Fragment
return false return false
} }
private fun loadingView(): LoadingView? { private fun loadingView(): LoadingView {
val loadingViewImpl = loadingView val loadingViewImpl = loadingView
if (loadingViewImpl == null) { return if (loadingViewImpl != null) {
loadingViewImpl
} else {
loadingView = onCreateLoadingView() 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? { protected open fun onCreateLoadingView(): LoadingView? {
@ -231,31 +232,31 @@ open class BaseFragment : Fragment(), LoadingView, OnBackPressListener, Fragment
} }
override fun showLoadingDialog() { override fun showLoadingDialog() {
loadingView()?.showLoadingDialog(true) loadingView().showLoadingDialog(true)
} }
override fun showLoadingDialog(cancelable: Boolean) { override fun showLoadingDialog(cancelable: Boolean) {
loadingView()?.showLoadingDialog(cancelable) loadingView().showLoadingDialog(cancelable)
} }
override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) { override fun showLoadingDialog(message: CharSequence, cancelable: Boolean) {
loadingView()?.showLoadingDialog(message, cancelable) loadingView().showLoadingDialog(message, cancelable)
} }
override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) { override fun showLoadingDialog(@StringRes messageId: Int, cancelable: Boolean) {
loadingView()?.showLoadingDialog(messageId, cancelable) loadingView().showLoadingDialog(messageId, cancelable)
} }
override fun dismissLoadingDialog() { override fun dismissLoadingDialog() {
loadingView()?.dismissLoadingDialog() loadingView().dismissLoadingDialog()
} }
override fun showMessage(message: CharSequence) { override fun showMessage(message: CharSequence) {
loadingView()?.showMessage(message) loadingView().showMessage(message)
} }
override fun showMessage(@StringRes messageId: Int) { override fun showMessage(@StringRes messageId: Int) {
loadingView()?.showMessage(messageId) loadingView().showMessage(messageId)
} }
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? { override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {

@ -44,7 +44,7 @@ abstract class BaseStateDialogFragment : BaseDialogFragment(), RefreshStateLayou
protected open fun onRetry(@StateLayoutConfig.RetryableState state: Int) { protected open fun onRetry(@StateLayoutConfig.RetryableState state: Int) {
refreshView.ifNonNull { refreshView.ifNonNull {
if (this.isRefreshing) { if (!this.isRefreshing) {
autoRefresh() autoRefresh()
} }
} otherwise { } otherwise {

@ -51,7 +51,7 @@ abstract class BaseStateFragment : BaseFragment(), RefreshStateLayout {
protected open fun onRetry(@RetryableState state: Int) { protected open fun onRetry(@RetryableState state: Int) {
refreshView.ifNonNull { refreshView.ifNonNull {
if (this.isRefreshing) { if (!this.isRefreshing) {
autoRefresh() autoRefresh()
} }
} otherwise { } otherwise {

@ -7,7 +7,6 @@ import android.view.animation.AnimationUtils;
import com.android.base.R; import com.android.base.R;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import timber.log.Timber;
public final class FragmentAnimatorHelper { public final class FragmentAnimatorHelper {
@ -17,7 +16,6 @@ public final class FragmentAnimatorHelper {
public FragmentAnimatorHelper(Context context, FragmentAnimator fragmentAnimator) { public FragmentAnimatorHelper(Context context, FragmentAnimator fragmentAnimator) {
this.context = context; this.context = context;
this.fragmentAnimator = fragmentAnimator; this.fragmentAnimator = fragmentAnimator;
Timber.d("context %s, animator %s", context.toString(), fragmentAnimator.toString());
} }
public void changeAnimation(FragmentAnimator fragmentAnimator) { public void changeAnimation(FragmentAnimator fragmentAnimator) {

@ -50,21 +50,25 @@ fun <H, T> H.handleLiveResourceWithData(
force: Boolean = true, force: Boolean = true,
onSuccess: (T) -> Unit onSuccess: (T) -> Unit
) where H : InjectableExtension, H : LoadingView, H : LifecycleOwner { ) where H : InjectableExtension, H : LoadingView, H : LifecycleOwner {
liveData.observe(this, Observer { liveData.observe(this, Observer {
when { when {
it.isError -> { it.isError -> {
Timber.d("handleLiveResourceWithData -> isError")
dismissLoadingDialog() dismissLoadingDialog()
errorHandler.handleError(it.error()) errorHandler.handleError(it.error())
} }
it.isLoading -> { it.isLoading -> {
Timber.d("handleLiveResourceWithData -> isLoading")
showLoadingDialog(!force) showLoadingDialog(!force)
} }
it.isSuccess && it.hasData() -> { it.isSuccess -> {
Timber.d("handleLiveResourceWithData -> isSuccess")
dismissLoadingDialog() dismissLoadingDialog()
if (it.hasData()) {
onSuccess(it.data()) onSuccess(it.data())
} }
} }
}
}) })
} }

@ -6,7 +6,7 @@ import com.android.base.app.Sword
import com.android.base.utils.common.isEmpty import com.android.base.utils.common.isEmpty
import timber.log.Timber import timber.log.Timber
fun <T> RefreshListLayout<T>.processListResultWithStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) { fun <T> RefreshListLayout<T>.handleListResultWithStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) {
if (isLoadingMore) { if (isLoadingMore) {
if (!isEmpty(list)) { if (!isEmpty(list)) {
addData(list) addData(list)
@ -31,7 +31,7 @@ fun <T> RefreshListLayout<T>.processListResultWithStatus(list: List<T>?, onEmpty
} }
} }
fun <T> RefreshListLayout<T>.processListResultWithoutStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) { fun <T> RefreshListLayout<T>.handleListResultWithoutStatus(list: List<T>?, onEmpty: (() -> Unit)? = null) {
if (isLoadingMore) { if (isLoadingMore) {
if (!isEmpty(list)) { if (!isEmpty(list)) {
addData(list) addData(list)
@ -83,7 +83,7 @@ fun <T> RefreshListLayout<T>.submitListResultWithoutStatus(list: List<T>?, hasMo
} }
} }
fun RefreshListLayout<*>.processListErrorWithStatus(throwable: Throwable) { fun RefreshListLayout<*>.handleListErrorWithStatus(throwable: Throwable) {
if (isRefreshing) { if (isRefreshing) {
refreshCompleted() refreshCompleted()
} }
@ -106,7 +106,7 @@ fun RefreshListLayout<*>.processListErrorWithStatus(throwable: Throwable) {
} }
} }
fun RefreshListLayout<*>.processListErrorWithoutStatus() { fun RefreshListLayout<*>.handleListErrorWithoutStatus() {
if (isRefreshing) { if (isRefreshing) {
refreshCompleted() refreshCompleted()
} }
@ -125,7 +125,7 @@ fun RefreshListLayout<*>.showLoadingIfEmpty() {
} }
} }
fun <T> RefreshStateLayout.processResultWithStatus(t: T?, onResult: ((T) -> Unit)) { fun <T> RefreshStateLayout.handleResultWithStatus(t: T?, onResult: ((T) -> Unit)) {
if (isRefreshing) { if (isRefreshing) {
refreshCompleted() refreshCompleted()
} }
@ -137,7 +137,7 @@ fun <T> RefreshStateLayout.processResultWithStatus(t: T?, onResult: ((T) -> Unit
} }
} }
fun RefreshStateLayout.processErrorWithStatus(throwable: Throwable?) { fun RefreshStateLayout.handleErrorWithStatus(throwable: Throwable?) {
if (throwable == null) { if (throwable == null) {
Timber.d("processErrorWithStatus called, but throwable is null") Timber.d("processErrorWithStatus called, but throwable is null")
return return

@ -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)
}

@ -11,12 +11,12 @@ import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import com.android.base.R; import com.android.base.R;
import com.android.base.app.ui.StateLayoutConfig;
import com.android.base.app.ui.StateLayoutConfig.ViewState; import com.android.base.app.ui.StateLayoutConfig.ViewState;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; 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.CONTENT;
import static com.android.base.app.ui.StateLayoutConfig.EMPTY; import static com.android.base.app.ui.StateLayoutConfig.EMPTY;
import static com.android.base.app.ui.StateLayoutConfig.ERROR; import static com.android.base.app.ui.StateLayoutConfig.ERROR;
@ -90,6 +90,7 @@ public class MultiStateView extends FrameLayout {
<enum name="net_error" value="5"/> <enum name="net_error" value="5"/>
<enum name="server_error" value="6"/> <enum name="server_error" value="6"/>
<enum name="requesting" value="7"/> <enum name="requesting" value="7"/>
<enum name="blank" value="8"/>
*/ */
switch (viewState) { switch (viewState) {
case 2: case 2:
@ -110,6 +111,9 @@ public class MultiStateView extends FrameLayout {
case 7: case 7:
mViewState = REQUESTING; mViewState = REQUESTING;
break; break;
case 8:
mViewState = BLANK;
break;
case 1: case 1:
default: default:
mViewState = CONTENT; mViewState = CONTENT;
@ -194,7 +198,7 @@ public class MultiStateView extends FrameLayout {
@Nullable @Nullable
@SuppressWarnings("unused") @SuppressWarnings("unused")
public View getView(@ViewState int state) { public View getView(@ViewState int state) {
if (state == StateLayoutConfig.BLANK) { if (state == BLANK) {
return null; return null;
} }
return ensureStateView(state); return ensureStateView(state);
@ -259,7 +263,7 @@ public class MultiStateView extends FrameLayout {
* Shows the {@link View} based on the {@link ViewState} * Shows the {@link View} based on the {@link ViewState}
*/ */
private void setView() { private void setView() {
if (mViewState == StateLayoutConfig.BLANK) { if (mViewState == BLANK) {
int size = mChildren.size(); int size = mChildren.size();
View view; View view;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {

@ -41,6 +41,7 @@
<enum name="net_error" value="5"/> <enum name="net_error" value="5"/>
<enum name="server_error" value="6"/> <enum name="server_error" value="6"/>
<enum name="requesting" value="7"/> <enum name="requesting" value="7"/>
<enum name="blank" value="8"/>
</attr> </attr>
</declare-styleable> </declare-styleable>

@ -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)
}
}
}
}

@ -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…
Cancel
Save