Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d10cf261 authored by Adam Powell's avatar Adam Powell Committed by android-build-merger
Browse files

Don't hold old host callback for fragments on the back stack am: 180202f2

am: 5254ef7b

Change-Id: I067c4fc441b34a7f9edfdbfee0995e91054fb890
parents e5d4c0b6 5254ef7b
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -340,6 +340,9 @@ public abstract class FragmentHostCallback<E> extends FragmentContainer {
    }

    void restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers) {
        for (int i = 0, N = loaderManagers.size(); i < N; i++) {
            ((LoaderManagerImpl) loaderManagers.valueAt(i)).updateHostController(this);
        }
        mAllLoaderManagers = loaderManagers;
    }

+7 −0
Original line number Diff line number Diff line
@@ -195,6 +195,9 @@ public abstract class LoaderManager {
    public static void enableDebugLogging(boolean enabled) {
        LoaderManagerImpl.DEBUG = enabled;
    }

    /** @hide for internal testing only */
    public FragmentHostCallback getFragmentHostCallback() { return null; }
}

class LoaderManagerImpl extends LoaderManager {
@@ -543,6 +546,10 @@ class LoaderManagerImpl extends LoaderManager {
        mHost = host;
    }

    public FragmentHostCallback getFragmentHostCallback() {
        return mHost;
    }
    
    private LoaderInfo createLoader(int id, Bundle args,
            LoaderManager.LoaderCallbacks<Object> callback) {
        LoaderInfo info = new LoaderInfo(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
+2 −0
Original line number Diff line number Diff line
@@ -1147,6 +1147,8 @@
        </activity>
        <activity android:name="com.android.internal.policy.PhoneWindowActionModeTestActivity">
        </activity>
        <activity android:name="android.app.EmptyActivity">
        </activity>

        <receiver android:name="android.app.activity.AbortReceiver">
            <intent-filter android:priority="1">
+21 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.app;

public class EmptyActivity extends Activity {
}
+225 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 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.app;

import android.content.Context;
import android.os.Handler;
import android.os.Parcelable;
import android.support.test.filters.MediumTest;
import android.support.test.rule.ActivityTestRule;
import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertNotSame;
import static junit.framework.TestCase.assertSame;

@RunWith(AndroidJUnit4.class)
public class LoaderLifecycleTest {
    @Rule
    public ActivityTestRule<EmptyActivity> mActivityRule =
            new ActivityTestRule<>(EmptyActivity.class);
    @Test
    @MediumTest
    public void loaderIdentityTest() throws Throwable{
        mActivityRule.runOnUiThread(() -> {
            final Handler h = new Handler();
            final FragmentController fc1 = FragmentController.createController(
                    new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));

            fc1.attachHost(null);
            fc1.dispatchCreate();

            final FragmentManager fm1 = fc1.getFragmentManager();

            final Fragment f1 = new Fragment();
            fm1.beginTransaction().add(f1, "one").commitNow();

            // Removing and re-adding a fragment completely will destroy its LoaderManager.
            // Keep the first one here to confirm this later.
            final LoaderManager lm1 = f1.getLoaderManager();

            // Remove the fragment, add a second one, and re-add the first to
            // force its internal index to change. The tests below should still remain consistent.
            final Fragment f2 = new Fragment();
            fm1.beginTransaction().remove(f1).commitNow();
            fm1.beginTransaction().add(f2, "two").commitNow();
            fm1.beginTransaction().add(f1, "one").commitNow();

            // We'll check this to see if we get the same instance back later
            // as passed through NonConfigurationInstance. If the keys stay consistent
            // across fragment remove/re-add, this will be consistent.
            final LoaderManager lm12 = f1.getLoaderManager();

            assertNotSame("fully removed and re-added fragment got same LoaderManager", lm1, lm12);

            fc1.dispatchActivityCreated();
            fc1.noteStateNotSaved();
            fc1.execPendingActions();
            fc1.doLoaderStart();
            fc1.dispatchStart();
            fc1.reportLoaderStart();
            fc1.dispatchResume();
            fc1.execPendingActions();

            // Bring the state back down to destroyed, simulating an activity restart
            fc1.dispatchPause();
            final Parcelable savedState = fc1.saveAllState();
            fc1.doLoaderStop(true);
            fc1.dispatchStop();
            final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();

            final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
            assertNotNull("loaderNonConfig was null", loaderNonConfig);

            fc1.dispatchDestroy();

            // Create the new controller and restore state
            final FragmentController fc2 = FragmentController.createController(
                    new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0));

            final FragmentManager fm2 = fc2.getFragmentManager();

            fc2.attachHost(null);
            fc2.restoreLoaderNonConfig(loaderNonConfig);
            fc2.restoreAllState(savedState, nonconf);
            fc2.dispatchCreate();


            fc2.dispatchActivityCreated();
            fc2.noteStateNotSaved();
            fc2.execPendingActions();
            fc2.doLoaderStart();
            fc2.dispatchStart();
            fc2.reportLoaderStart();
            fc2.dispatchResume();
            fc2.execPendingActions();

            // Test that the fragments are in the configuration we expect
            final Fragment restoredOne = fm2.findFragmentByTag("one");
            final LoaderManager lm2 = restoredOne.getLoaderManager();

            assertSame("didn't get same LoaderManager instance back", lm2, lm12);

            // Bring the state back down to destroyed before we finish the test
            fc2.dispatchPause();
            fc2.saveAllState();
            fc2.dispatchStop();
            fc2.dispatchDestroy();
        });
    }

    @Test
    @MediumTest
    public void backStackLoaderIdentityTest() throws Throwable{
        mActivityRule.runOnUiThread(() -> {
            final Handler h = new Handler();
            final FragmentHostCallback host1 =
                    new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
            final FragmentController fc1 = FragmentController.createController(host1);

            fc1.attachHost(null);
            fc1.dispatchCreate();

            final FragmentManager fm1 = fc1.getFragmentManager();

            final Fragment f1 = new Fragment();
            fm1.beginTransaction().add(f1, "one").commitNow();

            final LoaderManager lm1 = f1.getLoaderManager();

            // Put the fragment on the back stack.
            fm1.beginTransaction().remove(f1).addToBackStack("backentry").commit();
            fm1.executePendingTransactions();

            fc1.dispatchActivityCreated();
            fc1.noteStateNotSaved();
            fc1.execPendingActions();
            fc1.doLoaderStart();
            fc1.dispatchStart();
            fc1.reportLoaderStart();
            fc1.dispatchResume();
            fc1.execPendingActions();

            // Bring the state back down to destroyed, simulating an activity restart
            fc1.dispatchPause();
            final Parcelable savedState = fc1.saveAllState();
            fc1.doLoaderStop(true);
            fc1.dispatchStop();
            final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();

            final ArrayMap<String, LoaderManager> loaderNonConfig = fc1.retainLoaderNonConfig();
            assertNotNull("loaderNonConfig was null", loaderNonConfig);

            fc1.dispatchDestroy();

            // Create the new controller and restore state
            final FragmentHostCallback host2 =
                    new TestFragmentHostCallback(mActivityRule.getActivity(), h, 0);
            final FragmentController fc2 = FragmentController.createController(host2);

            final FragmentManager fm2 = fc2.getFragmentManager();

            fc2.attachHost(null);
            fc2.restoreLoaderNonConfig(loaderNonConfig);
            fc2.restoreAllState(savedState, nonconf);
            fc2.dispatchCreate();


            fc2.dispatchActivityCreated();
            fc2.noteStateNotSaved();
            fc2.execPendingActions();
            fc2.doLoaderStart();
            fc2.dispatchStart();
            fc2.reportLoaderStart();
            fc2.dispatchResume();
            fc2.execPendingActions();

            assertNotSame("LoaderManager kept reference to old FragmentHostCallback",
                    host1, lm1.getFragmentHostCallback());
            assertSame("LoaderManager did not refrence new FragmentHostCallback",
                    host2, lm1.getFragmentHostCallback());

            // Test that the fragments are in the configuration we expect
            final Fragment restoredOne = fm2.findFragmentByTag("one");
            final LoaderManager lm2 = restoredOne.getLoaderManager();

            assertSame("didn't get same LoaderManager instance back", lm2, lm1);

            // Bring the state back down to destroyed before we finish the test
            fc2.dispatchPause();
            fc2.saveAllState();
            fc2.dispatchStop();
            fc2.dispatchDestroy();
        });
    }

    public class TestFragmentHostCallback extends FragmentHostCallback<LoaderLifecycleTest> {
        public TestFragmentHostCallback(Context context, Handler handler, int windowAnimations) {
            super(context, handler, windowAnimations);
        }

        @Override
        public LoaderLifecycleTest onGetHost() {
            return LoaderLifecycleTest.this;
        }
    }
}