diff --git a/android/autodispose-android-archcomponents/build.gradle b/android/autodispose-android-archcomponents/build.gradle index 0771775ef..0969214a4 100644 --- a/android/autodispose-android-archcomponents/build.gradle +++ b/android/autodispose-android-archcomponents/build.gradle @@ -30,10 +30,7 @@ dependencies { androidTestImplementation project(':android:autodispose-android-archcomponents-test') androidTestImplementation project(':test-utils') - androidTestImplementation(deps.test.androidExtJunit) { - // https://issuetracker.google.com/issues/117486311 - exclude group: "org.junit" - } + androidTestImplementation deps.test.androidExtJunit androidTestImplementation deps.test.androidRunner androidTestImplementation deps.test.androidRules androidTestUtil deps.test.androidOrchestrator diff --git a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/KotlinExtensions.kt b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/KotlinExtensions.kt index edb084802..506f3bb31 100644 --- a/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/KotlinExtensions.kt +++ b/android/autodispose-android-archcomponents/src/main/java/com/uber/autodispose/android/lifecycle/KotlinExtensions.kt @@ -20,9 +20,22 @@ package com.uber.autodispose.android.lifecycle import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle.Event import androidx.lifecycle.LifecycleOwner +import com.uber.autodispose.AutoDispose +import com.uber.autodispose.FlowableSubscribeProxy +import com.uber.autodispose.ObservableSubscribeProxy +import com.uber.autodispose.SingleSubscribeProxy +import com.uber.autodispose.MaybeSubscribeProxy +import com.uber.autodispose.CompletableSubscribeProxy +import com.uber.autodispose.ParallelFlowableSubscribeProxy import com.uber.autodispose.ScopeProvider import com.uber.autodispose.lifecycle.CorrespondingEventsFunction import io.reactivex.annotations.CheckReturnValue +import io.reactivex.parallel.ParallelFlowable +import io.reactivex.Observable +import io.reactivex.Flowable +import io.reactivex.Maybe +import io.reactivex.Single +import io.reactivex.Completable /** * Extension that returns a [ScopeProvider] for this [LifecycleOwner]. @@ -77,3 +90,99 @@ inline fun Lifecycle.scope( boundaryResolver: CorrespondingEventsFunction ): ScopeProvider = AndroidLifecycleScopeProvider.from( this, boundaryResolver) + +/** + * Extension that proxies to [Flowable.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun Flowable.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): FlowableSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} + +/** + * Extension that proxies to [Observable.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun Observable.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): ObservableSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} + +/** + * Extension that proxies to [Single.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun Single.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): SingleSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} + +/** + * Extension that proxies to [Maybe.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun Maybe.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): MaybeSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} + +/** + * Extension that proxies to [Completable.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun Completable.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): CompletableSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} + +/** + * Extension that proxies to [ParallelFlowable.as] + [AutoDispose.autoDisposable] and takes an [untilEvent] when + * subscription will be disposed. + * + * @param lifecycleOwner The lifecycle owner. + * @param untilEvent Optional lifecycle event when subscription will be disposed. + */ +@CheckReturnValue +inline fun ParallelFlowable.autoDisposable(lifecycleOwner: LifecycleOwner, untilEvent: Event? = null): ParallelFlowableSubscribeProxy { + return if (untilEvent == null) { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner))) + } else { + this.`as`(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(lifecycleOwner, untilEvent))) + } +} diff --git a/android/autodispose-android/build.gradle b/android/autodispose-android/build.gradle index 45d8cea49..fae2d3c5e 100755 --- a/android/autodispose-android/build.gradle +++ b/android/autodispose-android/build.gradle @@ -28,10 +28,7 @@ dependencies { testImplementation deps.test.truth androidTestImplementation project(':test-utils') androidTestImplementation deps.androidx.annotations - androidTestImplementation(deps.test.androidExtJunit) { - // https://issuetracker.google.com/issues/117486311 - exclude group: "org.junit" - } + androidTestImplementation deps.test.androidExtJunit androidTestImplementation deps.test.androidRunner androidTestImplementation deps.test.androidRules androidTestUtil deps.test.androidOrchestrator diff --git a/android/autodispose-android/src/main/java/com/uber/autodispose/android/KotlinExtensions.kt b/android/autodispose-android/src/main/java/com/uber/autodispose/android/KotlinExtensions.kt index 19691ae1f..023f1e6db 100644 --- a/android/autodispose-android/src/main/java/com/uber/autodispose/android/KotlinExtensions.kt +++ b/android/autodispose-android/src/main/java/com/uber/autodispose/android/KotlinExtensions.kt @@ -18,11 +18,78 @@ package com.uber.autodispose.android import android.view.View +import com.uber.autodispose.AutoDispose +import com.uber.autodispose.FlowableSubscribeProxy +import com.uber.autodispose.ObservableSubscribeProxy +import com.uber.autodispose.SingleSubscribeProxy +import com.uber.autodispose.MaybeSubscribeProxy +import com.uber.autodispose.CompletableSubscribeProxy +import com.uber.autodispose.ParallelFlowableSubscribeProxy import com.uber.autodispose.ScopeProvider +import io.reactivex.Flowable +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.Maybe +import io.reactivex.Completable import io.reactivex.annotations.CheckReturnValue +import io.reactivex.parallel.ParallelFlowable /** * Extension that returns a [ScopeProvider] for this [View]. */ @CheckReturnValue inline fun View.scope(): ScopeProvider = ViewScopeProvider.from(this) + +/** + * Extension that proxies to [Flowable.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun Flowable.autoDisposable( + view: View +): FlowableSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) + +/** + * Extension that proxies to [Observable.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun Observable.autoDisposable( + view: View +): ObservableSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) + +/** + * Extension that proxies to [Single.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun Single.autoDisposable( + view: View +): SingleSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) + +/** + * Extension that proxies to [Maybe.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun Maybe.autoDisposable( + view: View +): MaybeSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) + +/** + * Extension that proxies to [Completable.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun Completable.autoDisposable( + view: View +): CompletableSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) + +/** + * Extension that proxies to [ParallelFlowable.as] + [AutoDispose.autoDisposable] + */ +@CheckReturnValue +inline fun ParallelFlowable.autoDisposable( + view: View +): ParallelFlowableSubscribeProxy = + this.`as`(AutoDispose.autoDisposable(ViewScopeProvider.from(view))) diff --git a/sample/build.gradle b/sample/build.gradle index c4bcd568f..644a745fb 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -30,6 +30,9 @@ android { defaultConfig { minSdkVersion deps.build.minSdkVersion targetSdkVersion deps.build.targetSdkVersion + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testApplicationId "com.uber.autodispose.android.lifecycle.androidTest" } compileOptions { sourceCompatibility deps.build.javaVersion @@ -51,6 +54,9 @@ android { matchingFallbacks = ['release'] } } + testOptions { + execution 'ANDROIDX_TEST_ORCHESTRATOR' + } // def classesWithScope = [ // "android.app.Activity", @@ -100,4 +106,10 @@ dependencies { def leakcanaryVersion = '1.6.2' debugImplementation "com.squareup.leakcanary:leakcanary-android:$leakcanaryVersion" releaseImplementation "com.squareup.leakcanary:leakcanary-android-no-op:$leakcanaryVersion" + + androidTestImplementation project(':test-utils') + androidTestImplementation deps.test.androidRunner + androidTestImplementation deps.test.androidRules + androidTestUtil deps.test.androidOrchestrator + androidTestImplementation deps.test.androidExtJunit } diff --git a/sample/src/androidTest/java/com/uber/autodispose/TestKotlinActivity.kt b/sample/src/androidTest/java/com/uber/autodispose/TestKotlinActivity.kt new file mode 100644 index 000000000..3ae54f1dd --- /dev/null +++ b/sample/src/androidTest/java/com/uber/autodispose/TestKotlinActivity.kt @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2019. Uber Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.uber.autodispose + +import android.os.Bundle +import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.Lifecycle +import com.uber.autodispose.android.autoDisposable +import com.uber.autodispose.android.lifecycle.AndroidLifecycleScopeProvider +import com.uber.autodispose.android.lifecycle.autoDisposable +import com.uber.autodispose.android.lifecycle.scope +import io.reactivex.Maybe +import io.reactivex.Observable +import io.reactivex.Single +import io.reactivex.Completable +import io.reactivex.CompletableSource +import io.reactivex.Flowable +import org.junit.Ignore +import java.util.concurrent.TimeUnit + +/** + * Test Activity class to verify compilation of various extension functions. + */ +@Ignore("Since it's only used to verify compilation of the extension functions") +class TestKotlinActivity : AppCompatActivity(), ScopeProvider { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // With extension function that overloads LifecycleOwner + Observable.interval(1, TimeUnit.SECONDS) + .autoDisposable(this) + .subscribe() + + // With extension function that overloads LifecycleOwner and until Event + Observable.interval(1, TimeUnit.SECONDS) + .autoDisposable(this, Lifecycle.Event.ON_DESTROY) + .subscribe() + + // With extension function that overloads ScopeProvider + Observable.interval(1, TimeUnit.SECONDS) + .autoDisposable(scope(Lifecycle.Event.ON_DESTROY)) + .subscribe() + + // With no extension function + Observable.interval(1, TimeUnit.SECONDS) + .autoDisposable(AndroidLifecycleScopeProvider.from(this)) + .subscribe() + + Maybe.just(1) + .autoDisposable(this) + .subscribe() + + Maybe.just(1) + .autoDisposable(this, Lifecycle.Event.ON_DESTROY) + .subscribe() + + Flowable.just(1) + .autoDisposable(this) + .subscribe() + + Flowable.just(1) + .autoDisposable(this, Lifecycle.Event.ON_DESTROY) + .subscribe() + + Single.just(1) + .autoDisposable(this) + .subscribe() + + Single.just(1) + .autoDisposable(this, Lifecycle.Event.ON_DESTROY) + .subscribe() + + Completable.never() + .autoDisposable(this) + .subscribe() + + Completable.never() + .autoDisposable(this, Lifecycle.Event.ON_DESTROY) + .subscribe() + + val rootView = findViewById(android.R.id.content) + + // Taking scope of a View + Observable.interval(1, TimeUnit.DAYS) + .autoDisposable(rootView) + .subscribe() + } + + override fun requestScope(): CompletableSource { + return Completable.complete() + } +}