Merge "Remove LifecycleDispatcher & Rename LifecycleRuntimeTrojanProvider" into oc-mr1-support-27.0-dev
diff --git a/lifecycle/extensions/src/main/AndroidManifest.xml b/lifecycle/extensions/src/main/AndroidManifest.xml
index f7698eb..f8a5c48 100644
--- a/lifecycle/extensions/src/main/AndroidManifest.xml
+++ b/lifecycle/extensions/src/main/AndroidManifest.xml
@@ -20,6 +20,6 @@
<provider android:authorities="${applicationId}.lifecycle-trojan"
android:multiprocess="true"
android:exported="false"
- android:name="android.arch.lifecycle.LifecycleRuntimeTrojanProvider"/>
+ android:name="android.arch.lifecycle.ProcessLifecycleOwnerInitializer"/>
</application>
</manifest>
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java
deleted file mode 100644
index 9fdec95..0000000
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleDispatcher.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * 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 android.arch.lifecycle;
-
-import static android.arch.lifecycle.Lifecycle.Event.ON_CREATE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_DESTROY;
-import static android.arch.lifecycle.Lifecycle.Event.ON_PAUSE;
-import static android.arch.lifecycle.Lifecycle.Event.ON_RESUME;
-import static android.arch.lifecycle.Lifecycle.Event.ON_START;
-import static android.arch.lifecycle.Lifecycle.Event.ON_STOP;
-import static android.arch.lifecycle.Lifecycle.State.CREATED;
-
-import android.app.Activity;
-import android.app.Application;
-import android.arch.lifecycle.Lifecycle.State;
-import android.content.Context;
-import android.os.Bundle;
-import android.support.annotation.VisibleForTesting;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-
-import java.util.Collection;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * When initialized, it hooks into the Activity callback of the Application and observes
- * Activities. It is responsible to hook in child-fragments to activities and fragments to report
- * their lifecycle events. Another responsibility of this class is to mark as stopped all lifecycle
- * providers related to an activity as soon it is not safe to run a fragment transaction in this
- * activity.
- */
-class LifecycleDispatcher {
-
- private static final String REPORT_FRAGMENT_TAG = "android.arch.lifecycle"
- + ".LifecycleDispatcher.report_fragment_tag";
-
- private static AtomicBoolean sInitialized = new AtomicBoolean(false);
-
- static void init(Context context) {
- if (sInitialized.getAndSet(true)) {
- return;
- }
- ((Application) context.getApplicationContext())
- .registerActivityLifecycleCallbacks(new DispatcherActivityCallback());
- }
-
- @SuppressWarnings("WeakerAccess")
- @VisibleForTesting
- static class DispatcherActivityCallback extends EmptyActivityLifecycleCallbacks {
- private final FragmentCallback mFragmentCallback;
-
- DispatcherActivityCallback() {
- mFragmentCallback = new FragmentCallback();
- }
-
- @Override
- public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- if (activity instanceof FragmentActivity) {
- ((FragmentActivity) activity).getSupportFragmentManager()
- .registerFragmentLifecycleCallbacks(mFragmentCallback, true);
- }
- ReportFragment.injectIfNeededIn(activity);
- }
-
- @Override
- public void onActivityStopped(Activity activity) {
- if (activity instanceof FragmentActivity) {
- markState((FragmentActivity) activity, CREATED);
- }
- }
-
- @Override
- public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
- if (activity instanceof FragmentActivity) {
- markState((FragmentActivity) activity, CREATED);
- }
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- public static class DestructionReportFragment extends Fragment {
- @Override
- public void onPause() {
- super.onPause();
- dispatch(ON_PAUSE);
- }
-
- @Override
- public void onStop() {
- super.onStop();
- dispatch(ON_STOP);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- dispatch(ON_DESTROY);
- }
-
- protected void dispatch(Lifecycle.Event event) {
- dispatchIfLifecycleOwner(getParentFragment(), event);
- }
- }
-
- private static void markState(FragmentManager manager, State state) {
- Collection<Fragment> fragments = manager.getFragments();
- if (fragments == null) {
- return;
- }
- for (Fragment fragment : fragments) {
- if (fragment == null) {
- continue;
- }
- markStateIn(fragment, state);
- if (fragment.isAdded()) {
- markState(fragment.getChildFragmentManager(), state);
- }
- }
- }
-
- private static void markStateIn(Object object, State state) {
- if (object instanceof LifecycleRegistryOwner) {
- LifecycleRegistry registry = ((LifecycleRegistryOwner) object).getLifecycle();
- registry.markState(state);
- }
- }
-
- private static void markState(FragmentActivity activity, State state) {
- markStateIn(activity, state);
- markState(activity.getSupportFragmentManager(), state);
- }
-
- private static void dispatchIfLifecycleOwner(Fragment fragment, Lifecycle.Event event) {
- if (fragment instanceof LifecycleRegistryOwner) {
- ((LifecycleRegistryOwner) fragment).getLifecycle().handleLifecycleEvent(event);
- }
- }
-
- @SuppressWarnings("WeakerAccess")
- @VisibleForTesting
- static class FragmentCallback extends FragmentManager.FragmentLifecycleCallbacks {
-
- @Override
- public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
- dispatchIfLifecycleOwner(f, ON_CREATE);
-
- if (!(f instanceof LifecycleRegistryOwner)) {
- return;
- }
-
- if (f.getChildFragmentManager().findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
- f.getChildFragmentManager().beginTransaction().add(new DestructionReportFragment(),
- REPORT_FRAGMENT_TAG).commit();
- }
- }
-
- @Override
- public void onFragmentStarted(FragmentManager fm, Fragment f) {
- dispatchIfLifecycleOwner(f, ON_START);
- }
-
- @Override
- public void onFragmentResumed(FragmentManager fm, Fragment f) {
- dispatchIfLifecycleOwner(f, ON_RESUME);
- }
- }
-}
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java
index ec8274c..179e2c4 100644
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwner.java
@@ -157,7 +157,8 @@
app.registerActivityLifecycleCallbacks(new EmptyActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
- ReportFragment .get(activity).setProcessListener(mInitializationListener);
+ ReportFragment.injectIfNeededIn(activity);
+ ReportFragment.get(activity).setProcessListener(mInitializationListener);
}
@Override
diff --git a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
similarity index 93%
rename from lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
rename to lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
index ac278c0..8ba297f 100644
--- a/lifecycle/extensions/src/main/java/android/arch/lifecycle/LifecycleRuntimeTrojanProvider.java
+++ b/lifecycle/extensions/src/main/java/android/arch/lifecycle/ProcessLifecycleOwnerInitializer.java
@@ -29,10 +29,9 @@
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-public class LifecycleRuntimeTrojanProvider extends ContentProvider {
+public class ProcessLifecycleOwnerInitializer extends ContentProvider {
@Override
public boolean onCreate() {
- LifecycleDispatcher.init(getContext());
ProcessLifecycleOwner.init(getContext());
return true;
}
diff --git a/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java b/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java
deleted file mode 100644
index 86b25b6..0000000
--- a/lifecycle/extensions/src/test/java/android/arch/lifecycle/DispatcherActivityCallbackTest.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * 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 android.arch.lifecycle;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.app.Fragment;
-import android.app.FragmentTransaction;
-import android.os.Bundle;
-import android.support.v4.app.FragmentActivity;
-import android.support.v4.app.FragmentManager;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
-
-@RunWith(JUnit4.class)
-public class DispatcherActivityCallbackTest {
- @Test
- public void onCreateFrameworkActivity() {
- LifecycleDispatcher.DispatcherActivityCallback callback =
- new LifecycleDispatcher.DispatcherActivityCallback();
- Activity activity = mock(Activity.class);
- checkReportFragment(callback, activity);
- }
-
- @Test
- public void onCreateFragmentActivity() {
- LifecycleDispatcher.DispatcherActivityCallback callback =
- new LifecycleDispatcher.DispatcherActivityCallback();
- FragmentActivity activity = mock(FragmentActivity.class);
- FragmentManager fragmentManager = mock(FragmentManager.class);
- when(activity.getSupportFragmentManager()).thenReturn(fragmentManager);
-
- checkReportFragment(callback, activity);
-
- verify(activity).getSupportFragmentManager();
- verify(fragmentManager).registerFragmentLifecycleCallbacks(
- any(FragmentManager.FragmentLifecycleCallbacks.class), eq(true));
- }
-
- @SuppressLint("CommitTransaction")
- private void checkReportFragment(LifecycleDispatcher.DispatcherActivityCallback callback,
- Activity activity) {
- android.app.FragmentManager fm = mock(android.app.FragmentManager.class);
- FragmentTransaction transaction = mock(FragmentTransaction.class);
- when(activity.getFragmentManager()).thenReturn(fm);
- when(fm.beginTransaction()).thenReturn(transaction);
- when(transaction.add(any(Fragment.class), anyString())).thenReturn(transaction);
- callback.onActivityCreated(activity, mock(Bundle.class));
- verify(activity).getFragmentManager();
- verify(fm).beginTransaction();
- verify(transaction).add(any(ReportFragment.class), anyString());
- verify(transaction).commit();
- }
-}
diff --git a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java
index 37bdcdb..77baf94 100644
--- a/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java
+++ b/lifecycle/integration-tests/testapp/src/androidTest/java/android/arch/lifecycle/ProcessOwnerTest.java
@@ -31,6 +31,7 @@
import android.arch.lifecycle.testapp.NavigationDialogActivity;
import android.arch.lifecycle.testapp.NavigationTestActivityFirst;
import android.arch.lifecycle.testapp.NavigationTestActivitySecond;
+import android.arch.lifecycle.testapp.NonSupportActivity;
import android.content.Context;
import android.content.Intent;
import android.support.test.InstrumentationRegistry;
@@ -95,6 +96,22 @@
}
@Test
+ public void testNavigationToNonSupport() throws Throwable {
+ FragmentActivity firstActivity = setupObserverOnResume();
+ Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+ NonSupportActivity.class.getCanonicalName(), null, false);
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ instrumentation.addMonitor(monitor);
+
+ Intent intent = new Intent(firstActivity, NonSupportActivity.class);
+ firstActivity.finish();
+ firstActivity.startActivity(intent);
+ NonSupportActivity secondActivity = (NonSupportActivity) monitor.waitForActivity();
+ assertThat("Failed to navigate", secondActivity, notNullValue());
+ checkProcessObserverSilent(secondActivity);
+ }
+
+ @Test
public void testRecreation() throws Throwable {
FragmentActivity activity = setupObserverOnResume();
FragmentActivity recreated = TestUtils.recreateActivity(activity, activityTestRule);
@@ -164,4 +181,11 @@
activityTestRule.runOnUiThread(() ->
ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
}
+
+ private void checkProcessObserverSilent(NonSupportActivity activity) throws Throwable {
+ assertThat(activity.awaitResumedState(), is(true));
+ assertThat(mObserver.mChangedState, is(false));
+ activityTestRule.runOnUiThread(() ->
+ ProcessLifecycleOwner.get().getLifecycle().removeObserver(mObserver));
+ }
}
diff --git a/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
index bf88f97..50f2924 100644
--- a/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
+++ b/lifecycle/integration-tests/testapp/src/main/AndroidManifest.xml
@@ -71,5 +71,6 @@
<activity android:name=".NavigationDialogActivity"
android:launchMode="singleTask"
android:theme="@android:style/Theme.DeviceDefault.Dialog" />
+ <activity android:name=".NonSupportActivity"/>
</application>
</manifest>
diff --git a/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NonSupportActivity.java b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NonSupportActivity.java
new file mode 100644
index 0000000..835d846
--- /dev/null
+++ b/lifecycle/integration-tests/testapp/src/main/java/android/arch/lifecycle/testapp/NonSupportActivity.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * 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 android.arch.lifecycle.testapp;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Activity which doesn't extend FragmentActivity, to test ProcessLifecycleOwner because it
+ * should work anyway.
+ */
+public class NonSupportActivity extends Activity {
+
+ private static final int TIMEOUT = 1; //secs
+ private final Lock mLock = new ReentrantLock();
+ private Condition mIsResumedCondition = mLock.newCondition();
+ private boolean mIsResumed = false;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mLock.lock();
+ try {
+ mIsResumed = true;
+ mIsResumedCondition.signalAll();
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mLock.lock();
+ try {
+ mIsResumed = false;
+ } finally {
+ mLock.unlock();
+ }
+ }
+
+ /**
+ * awaits resumed state
+ * @return
+ * @throws InterruptedException
+ */
+ public boolean awaitResumedState() throws InterruptedException {
+ mLock.lock();
+ try {
+ while (!mIsResumed) {
+ if (!mIsResumedCondition.await(TIMEOUT, TimeUnit.SECONDS)) {
+ return false;
+ }
+ }
+ return true;
+ } finally {
+ mLock.unlock();
+ }
+ }
+}