Comments (7)
I'd be open to adding this to the Android artifact. Just simple show/dismiss to keep things simple should be fine. Want to contribute a PR?
from autodispose.
After I wrote some codes for this artifact. I realize Dialog only has the setShowListener() and setDissmissListener() , not the addShowListener() and addDissmissListener() , so we need to custom OnShowListener and OnDissmissListener interface. It's bad.
So why not use the ViewScopeProvider.from(Dialog.getWindow().getDecordView()) instead ?
But there is a problem. After i call Dialog.show(), dialog DecordView state is still Detached, the ViewScopeProvider throws
OutsideLifecycleException("View is detached!")
So hope to change ViewScopeProvider.java
public class ViewScopeProvider implements LifecycleScopeProvider<ViewLifecycleEvent> {
private static final Function<ViewLifecycleEvent, ViewLifecycleEvent> CORRESPONDING_EVENTS =
new Function<ViewLifecycleEvent, ViewLifecycleEvent>() {
@Override public ViewLifecycleEvent apply(ViewLifecycleEvent lastEvent) throws Exception {
switch (lastEvent) {
case ATTACH:
return DETACH;
default:
throw new OutsideLifecycleException("View is detached!");
}
}
};
to
public class ViewScopeProvider implements LifecycleScopeProvider<ViewLifecycleEvent> {
private static final Function<ViewLifecycleEvent, ViewLifecycleEvent> CORRESPONDING_EVENTS =
new Function<ViewLifecycleEvent, ViewLifecycleEvent>() {
@Override public ViewLifecycleEvent apply(ViewLifecycleEvent lastEvent) throws Exception {
switch (lastEvent) {
case ATTACH:
return DETACH;
default:
// throw new OutsideLifecycleException("View is detached!");
return DETACH;
}
}
};
from autodispose.
That change is incorrect behavior as the exception is being thrown correctly. The reason you get that exception is because window changes are actually asynchronous, so it is actually detached at the time. You would have to delay subscription until attach. I don't think decorview attaches is the correct way to handle dialogs anyway.
I don't think a setter is terrible, but it is less than ideal. I'd be fine with putting an example using the setter in the sample app.
from autodispose.
Maybe we can modify ViewScopeProvider.java like this
public class ViewScopeProvider implements LifecycleScopeProvider<ViewLifecycleEvent> {
//add
private final Dialog dalog;
//add
public static LifecycleScopeProvider from(Dialog dialog) {
if (dialog == null) {
throw new NullPointerException("dialog == null");
}
return new ViewScopeProvider(dialog);
}
//add
private ViewScopeProvider(final Dialog dialog) {
this.dialog = dialog;
this.view = dialog.getWindow().getDecordView();
lifecycle = new ViewAttachEventsObservable(view);
}
//modify
@Override public ViewLifecycleEvent peekLifecycle() {
if(dialog != null){
return dialog.isShowing() ? ViewLifecycleEvent.ATTACH : DETACH;
}
return AutoDisposeAndroidUtil.isAttached(view) ? ViewLifecycleEvent.ATTACH : DETACH;
}
from autodispose.
No, ViewScopeProvider should not know anything or care about dialogs
from autodispose.
I have written the codes
DialogScopeProvider.java , DialogScopeProviderTest.java and AutoDisposeTestActivity.java
DialogScopeProvider.java
/*
* Copyright (C) 2017. 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.android;
import android.app.Dialog;
import com.uber.autodispose.LifecycleScopeProvider;
import com.uber.autodispose.OutsideLifecycleException;
import com.uber.autodispose.android.internal.AutoDisposeAndroidUtil;
import io.reactivex.Observable;
import io.reactivex.functions.Function;
import static com.uber.autodispose.android.ViewLifecycleEvent.DETACH;
/**
* A {@link LifecycleScopeProvider} that can provide scoping for Android {@link Dialog} classes.
* <p>
* <pre><code>
* AutoDispose.with(DialogScopeProvider.from(dialog));
* </code></pre>
*/
public class DialogScopeProvider implements LifecycleScopeProvider<ViewLifecycleEvent> {
private static final Function<ViewLifecycleEvent, ViewLifecycleEvent> CORRESPONDING_EVENTS =
new Function<ViewLifecycleEvent, ViewLifecycleEvent>() {
@Override public ViewLifecycleEvent apply(ViewLifecycleEvent lastEvent) throws Exception {
switch (lastEvent) {
case ATTACH:
return DETACH;
default:
throw new OutsideLifecycleException("Dialog is dismiss!");
}
}
};
private final Observable<ViewLifecycleEvent> lifecycle;
private final Dialog dialog;
/**
* Creates a {@link LifecycleScopeProvider} for Android Dialog.
*
* @param dialog the dialog to scope for
* @return a {@link LifecycleScopeProvider} against this dialog.
*/
public static DialogScopeProvider from(Dialog dialog) {
if (dialog == null) {
throw new NullPointerException("dialog == null");
}
return new DialogScopeProvider(dialog);
}
private DialogScopeProvider(final Dialog dialog) {
this.dialog = dialog;
lifecycle = new ViewAttachEventsObservable(dialog.getWindow().getDecorView());
}
@Override public Observable<ViewLifecycleEvent> lifecycle() {
return lifecycle;
}
@Override public Function<ViewLifecycleEvent, ViewLifecycleEvent> correspondingEvents() {
return CORRESPONDING_EVENTS;
}
@Override public ViewLifecycleEvent peekLifecycle() {
if (dialog.isShowing() || AutoDisposeAndroidUtil.isAttached(
dialog.getWindow().getDecorView())) {
return ViewLifecycleEvent.ATTACH;
}
return ViewLifecycleEvent.DETACH;
}
}
DialogScopeProviderTest.java
/*
* Copyright (C) 2017. 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.android;
import android.app.Dialog;
import android.app.Instrumentation;
import android.support.test.InstrumentationRegistry;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.uber.autodispose.AutoDispose;
import com.uber.autodispose.OutsideLifecycleException;
import com.uber.autodispose.android.internal.AutoDisposeAndroidUtil;
import com.uber.autodispose.test.RecordingObserver;
import io.reactivex.disposables.Disposable;
import io.reactivex.subjects.PublishSubject;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.google.common.truth.Truth.assertThat;
@RunWith(AndroidJUnit4.class) public final class DialogScopeProviderTest {
private static final RecordingObserver.Logger LOGGER = new RecordingObserver.Logger() {
@Override public void log(String message) {
Log.d(DialogScopeProviderTest.class.getSimpleName(), message);
}
};
@Rule public final ActivityTestRule<AutoDisposeTestActivity> activityRule =
new ActivityTestRule<>(AutoDisposeTestActivity.class);
private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
private Dialog dialog;
@Before public void setUp() {
AutoDisposeTestActivity activity = activityRule.getActivity();
dialog = activity.dialog;
}
@Test public void observable_normal() {
final RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
final PublishSubject<Integer> subject = PublishSubject.create();
// Show dialog
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
dialog.show();
}
});
assertThat(dialog.isShowing()).isTrue();
assertThat(AutoDisposeAndroidUtil.isAttached(dialog.getWindow().getDecorView())).isFalse();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
subject.to(AutoDispose.with(DialogScopeProvider.from(dialog)).<Integer>forObservable())
.subscribe(o);
}
});
Disposable d = o.takeSubscribe();
o.assertNoMoreEvents(); // No initial value.
subject.onNext(0);
assertThat(o.takeNext()).isEqualTo(0);
subject.onNext(1);
assertThat(o.takeNext()).isEqualTo(1);
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
dialog.dismiss();
}
});
assertThat(dialog.isShowing()).isFalse();
assertThat(AutoDisposeAndroidUtil.isAttached(dialog.getWindow().getDecorView())).isFalse();
subject.onNext(2);
o.assertNoMoreEvents();
d.dispose();
}
@Test public void observable_offMainThread_shouldFail() {
RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
PublishSubject<Integer> subject = PublishSubject.create();
// Show dialog
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
dialog.show();
}
});
assertThat(dialog.isShowing()).isTrue();
assertThat(AutoDisposeAndroidUtil.isAttached(dialog.getWindow().getDecorView())).isFalse();
subject.to(AutoDispose.with(DialogScopeProvider.from(dialog)).<Integer>forObservable())
.subscribe(o);
Disposable d = o.takeSubscribe();
Throwable t = o.takeError();
assertThat(t).isInstanceOf(IllegalStateException.class);
assertThat(t.getMessage()).contains("main thread");
o.assertNoMoreEvents();
assertThat(d.isDisposed()).isTrue();
}
@Test public void observable_offBeforeAttach_shouldFail() {
final RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
final PublishSubject<Integer> subject = PublishSubject.create();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
subject.to(AutoDispose.with(DialogScopeProvider.from(dialog)).<Integer>forObservable())
.subscribe(o);
}
});
Disposable d = o.takeSubscribe();
Throwable t = o.takeError();
assertThat(t).isInstanceOf(OutsideLifecycleException.class);
o.assertNoMoreEvents();
assertThat(d.isDisposed()).isTrue();
}
@Test public void observable_offAfterDetach_shouldFail() {
final RecordingObserver<Integer> o = new RecordingObserver<>(LOGGER);
final PublishSubject<Integer> subject = PublishSubject.create();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
dialog.show();
}
});
assertThat(dialog.isShowing()).isTrue();
assertThat(AutoDisposeAndroidUtil.isAttached(dialog.getWindow().getDecorView())).isFalse();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
dialog.dismiss();
}
});
assertThat(dialog.isShowing()).isFalse();
assertThat(AutoDisposeAndroidUtil.isAttached(dialog.getWindow().getDecorView())).isFalse();
instrumentation.runOnMainSync(new Runnable() {
@Override public void run() {
subject.to(AutoDispose.with(DialogScopeProvider.from(dialog)).<Integer>forObservable())
.subscribe(o);
}
});
Disposable d = o.takeSubscribe();
Throwable t = o.takeError();
assertThat(t).isInstanceOf(OutsideLifecycleException.class);
o.assertNoMoreEvents();
assertThat(d.isDisposed()).isTrue();
}
}
AutoDisposeTestActivity.java
/*
* Copyright (C) 2017. 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.android;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.os.Bundle;
import android.view.View;
import android.widget.FrameLayout;
@SuppressWarnings("NullAway") // https://github.com/uber/NullAway/issues/19
public final class AutoDisposeTestActivity extends Activity {
FrameLayout parent;
View child;
Dialog dialog;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
parent = new FrameLayout(this);
child = new View(this);
setContentView(parent);
dialog = new AlertDialog.Builder(this).setTitle("DialogScopeProviderTest").create();
}
}
from autodispose.
you're welcome to use that code in your own project, but as mentioned above, decor view is not the correct approach for this. I'm going to close this out
from autodispose.
Related Issues (20)
- crash by fragment in removing state HOT 1
- ViewScopeProvider throws when Activity restarts. HOT 5
- Question: Incompatible with RxJava 2? HOT 4
- Unsubscribe unexpectedly when used in item of 'RecyclerView' inside 'ViewPager2' after page changed. HOT 1
- how to remove warning Missing Disposable handling: Apply AutoDispose or cache the Disposable instance manually and enable lenient mode. HOT 1
- I need AutoDispose proguard
- Help ,Why the main thread is required ? HOT 3
- Integrate kotlin ABI validator
- Please ignore
- Please ignore
- Obseravable called twice when completable scope. #416 HOT 3
- Unusable with lint 7.0.0-alpha14 (Jetpack Compose project) HOT 4
- How to use it in MVVM? HOT 2
- gradle 7.1 and AGP 7.0.0-beta05 HOT 3
- Snapshots and Building from source HOT 1
- Leak - DetachEventCompletable isn't removed when View is removed from ViewTree HOT 1
- lint lenient mode do not support kotlin lambda/when/if expressions HOT 1
- Question: how to implement a custom LifecycleScopeProvider HOT 1
- ERROR: R8: Missing class com.google.errorprone.annotations.DoNotMock (referenced from: autodispose2.ScopeProvider and 1 other context) HOT 3
- Service loader file missing in error prone plugin
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from autodispose.