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

Commit a59ac9cd authored by Filip Gruszczynski's avatar Filip Gruszczynski
Browse files

Preserve window during resize triggered relaunches.

This changes application code behavior when the activity relaunches due
to configuration change. It only applies to scenarios, where the
configuration change was triggered by a user generated resize of an
activity (i.e. user drags a corner of an activity and thus changes its
size).

Preserving a window means that we will keep the decor view and non
client decor view around, but remove all children views when the
activity gets destroyed. When the activity gets created again, it will
attach its new content to the preserved view hierarchy. Mind, we
actually recreate application side Window object, since some of its
features might changed, but we retain its elevation (to not trigger
relayout with new layout params).

Preserving the window also means that we don't call the window manager
service to remove and later add the window. Instead, we continue using a
single window state throughout the resize operation.

Change-Id: Ie3d2878ed09c99ff343044bfe7a29a0ba07a265e
parent ca9f413b
Loading
Loading
Loading
Loading
+13 −13
Original line number Diff line number Diff line
@@ -2266,12 +2266,12 @@ public class Am extends BaseCommand {
            System.err.println("Error: invalid input bounds");
            return;
        }
        taskResize(taskId, bounds, 0);
        taskResize(taskId, bounds, 0, false);
    }

    private void taskResize(int taskId, Rect bounds, int delay_ms) {
    private void taskResize(int taskId, Rect bounds, int delay_ms, boolean pretendUserResize) {
        try {
            mAm.resizeTask(taskId, bounds);
            mAm.resizeTask(taskId, bounds, pretendUserResize);
            Thread.sleep(delay_ms);
        } catch (RemoteException e) {
            System.err.println("Error changing task bounds: " + e);
@@ -2354,7 +2354,7 @@ public class Am extends BaseCommand {
                    taskRect.top += maxMove;
                    taskRect.bottom += maxMove;
                }
                taskResize(taskId, taskRect, delay_ms);
                taskResize(taskId, taskRect, delay_ms, false);
            }
        } else {
            while (maxToTravel < 0
@@ -2371,7 +2371,7 @@ public class Am extends BaseCommand {
                    taskRect.top -= maxMove;
                    taskRect.bottom -= maxMove;
                }
                taskResize(taskId, taskRect, delay_ms);
                taskResize(taskId, taskRect, delay_ms, false);
            }
        }
        // Return the remaining distance we didn't travel because we reached the target location.
@@ -2405,7 +2405,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.left -= getStepSize(
                    currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (stackBounds.top < currentTaskBounds.top
                || stackBounds.left < currentTaskBounds.left);

@@ -2418,7 +2418,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.left += getStepSize(
                    currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (initialTaskBounds.top > currentTaskBounds.top
                || initialTaskBounds.left > currentTaskBounds.left);

@@ -2431,7 +2431,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.right += getStepSize(
                    currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (stackBounds.top < currentTaskBounds.top
                || stackBounds.right > currentTaskBounds.right);

@@ -2444,7 +2444,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
                    stepSize, GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (initialTaskBounds.top > currentTaskBounds.top
                || initialTaskBounds.right < currentTaskBounds.right);

@@ -2457,7 +2457,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.left -= getStepSize(
                    currentTaskBounds.left, stackBounds.left, stepSize, GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (stackBounds.bottom > currentTaskBounds.bottom
                || stackBounds.left < currentTaskBounds.left);

@@ -2470,7 +2470,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.left += getStepSize(
                    currentTaskBounds.left, initialTaskBounds.left, stepSize, !GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (initialTaskBounds.bottom < currentTaskBounds.bottom
                || initialTaskBounds.left > currentTaskBounds.left);

@@ -2483,7 +2483,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.right += getStepSize(
                    currentTaskBounds.right, stackBounds.right, stepSize, !GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (stackBounds.bottom > currentTaskBounds.bottom
                || stackBounds.right > currentTaskBounds.right);

@@ -2496,7 +2496,7 @@ public class Am extends BaseCommand {
            currentTaskBounds.right -= getStepSize(currentTaskBounds.right, initialTaskBounds.right,
                    stepSize, GREATER_THAN_TARGET);

            taskResize(taskId, currentTaskBounds, delay_ms);
            taskResize(taskId, currentTaskBounds, delay_ms, true);
        } while (initialTaskBounds.bottom < currentTaskBounds.bottom
                || initialTaskBounds.right < currentTaskBounds.right);
    }
+12 −6
Original line number Diff line number Diff line
@@ -4879,7 +4879,8 @@ public class Activity extends ContextThemeWrapper
        if (Looper.myLooper() != mMainThread.getLooper()) {
            throw new IllegalStateException("Must be called from main thread");
        }
        mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false);
        mMainThread.requestRelaunchActivity(mToken, null, null, 0, false, null, null, false,
                false /* preserveWindow */);
    }

    /**
@@ -6223,12 +6224,13 @@ public class Activity extends ContextThemeWrapper
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window) {
        attachBaseContext(context);

        mFragments.attachHost(null /*parent*/);

        mWindow = new PhoneWindow(this);
        mWindow = new PhoneWindow(this, window);
        mWindow.setWindowControllerCallback(this);
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
@@ -6317,12 +6319,16 @@ public class Activity extends ContextThemeWrapper
    final void performRestart() {
        mFragments.noteStateNotSaved();

        if (mStopped) {
            mStopped = false;
        if (mToken != null && mParent == null) {
                WindowManagerGlobal.getInstance().setStoppedState(mToken, false);
            // We might have view roots that were preserved during a relaunch, we need to start them
            // again. We don't need to check mStopped, the roots will check if they were actually
            // stopped.
            WindowManagerGlobal.getInstance().setStoppedState(mToken, false /* stopped */);
        }

        if (mStopped) {
            mStopped = false;

            synchronized (mManagedCursors) {
                final int N = mManagedCursors.size();
                for (int i=0; i<N; i++) {
+4 −2
Original line number Diff line number Diff line
@@ -2452,8 +2452,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
        case RESIZE_TASK_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            int taskId = data.readInt();
            final boolean resizedByUser = data.readInt() == 1;
            Rect r = Rect.CREATOR.createFromParcel(data);
            resizeTask(taskId, r);
            resizeTask(taskId, r, resizedByUser);
            reply.writeNoException();
            return true;
        }
@@ -5899,12 +5900,13 @@ class ActivityManagerProxy implements IActivityManager
    }

    @Override
    public void resizeTask(int taskId, Rect r) throws RemoteException
    public void resizeTask(int taskId, Rect r, boolean resizedByUser) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeInt(taskId);
        data.writeInt(resizedByUser ? 1 : 0);
        r.writeToParcel(data, 0);
        mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
        reply.readException();
+43 −14
Original line number Diff line number Diff line
@@ -315,8 +315,9 @@ public final class ActivityThread {
        int pendingConfigChanges;
        boolean onlyLocalRequest;

        View mPendingRemoveWindow;
        Window mPendingRemoveWindow;
        WindowManager mPendingRemoveWindowManager;
        boolean mPreserveWindow;

        ActivityClientRecord() {
            parent = null;
@@ -670,9 +671,9 @@ public final class ActivityThread {
        public final void scheduleRelaunchActivity(IBinder token,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                int configChanges, boolean notResumed, Configuration config,
                Configuration overrideConfig) {
                Configuration overrideConfig, boolean preserveWindow) {
            requestRelaunchActivity(token, pendingResults, pendingNewIntents,
                    configChanges, notResumed, config, overrideConfig, true);
                    configChanges, notResumed, config, overrideConfig, true, preserveWindow);
        }

        public final void scheduleNewIntent(List<ReferrerIntent> intents, IBinder token) {
@@ -2376,10 +2377,16 @@ public final class ActivityThread {
                Configuration config = new Configuration(mCompatConfiguration);
                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                        + r.activityInfo.name + " with config " + config);
                Window window = null;
                if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
                    window = r.mPendingRemoveWindow;
                    r.mPendingRemoveWindow = null;
                    r.mPendingRemoveWindowManager = null;
                }
                activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor);
                        r.referrer, r.voiceInteractor, window);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
@@ -3191,10 +3198,14 @@ public final class ActivityThread {
        return r;
    }

    static final void cleanUpPendingRemoveWindows(ActivityClientRecord r) {
    static final void cleanUpPendingRemoveWindows(ActivityClientRecord r, boolean force) {
        if (r.mPreserveWindow && !force) {
            return;
        }
        if (r.mPendingRemoveWindow != null) {
            r.mPendingRemoveWindowManager.removeViewImmediate(r.mPendingRemoveWindow);
            IBinder wtoken = r.mPendingRemoveWindow.getWindowToken();
            r.mPendingRemoveWindowManager.removeViewImmediate(
                    r.mPendingRemoveWindow.getDecorView());
            IBinder wtoken = r.mPendingRemoveWindow.getDecorView().getWindowToken();
            if (wtoken != null) {
                WindowManagerGlobal.getInstance().closeAll(wtoken,
                        r.activity.getClass().getName(), "Activity");
@@ -3245,7 +3256,11 @@ public final class ActivityThread {
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
@@ -3260,7 +3275,7 @@ public final class ActivityThread {
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r);
            cleanUpPendingRemoveWindows(r, false /* force */);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
@@ -3745,7 +3760,8 @@ public final class ActivityThread {

            // request all activities to relaunch for the changes to take place
            for (Map.Entry<IBinder, ActivityClientRecord> entry : mActivities.entrySet()) {
                requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false);
                requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false,
                        false /* preserveWindow */);
            }
        }
    }
@@ -3931,7 +3947,7 @@ public final class ActivityThread {
        ActivityClientRecord r = performDestroyActivity(token, finishing,
                configChanges, getNonConfigInstance);
        if (r != null) {
            cleanUpPendingRemoveWindows(r);
            cleanUpPendingRemoveWindows(r, finishing);
            WindowManager wm = r.activity.getWindowManager();
            View v = r.activity.mDecor;
            if (v != null) {
@@ -3940,11 +3956,18 @@ public final class ActivityThread {
                }
                IBinder wtoken = v.getWindowToken();
                if (r.activity.mWindowAdded) {
                    if (r.onlyLocalRequest) {
                    boolean reuseForResize = r.window.hasNonClientDecorView() && r.mPreserveWindow;
                    if (r.onlyLocalRequest || reuseForResize) {
                        // Hold off on removing this until the new activity's
                        // window is being added.
                        r.mPendingRemoveWindow = v;
                        r.mPendingRemoveWindow = r.window;
                        r.mPendingRemoveWindowManager = wm;
                        if (reuseForResize) {
                            // We can only keep the part of the view hierarchy that we control,
                            // everything else must be removed, because it might not be able to
                            // behave properly when activity is relaunching.
                            r.window.clearContentView();
                        }
                    } else {
                        wm.removeViewImmediate(v);
                    }
@@ -3986,10 +4009,14 @@ public final class ActivityThread {
        mSomeActivitiesChanged = true;
    }

    /**
     * @param preserveWindow Whether the activity should try to reuse the window it created,
     *                        including the decor view after the relaunch.
     */
    public final void requestRelaunchActivity(IBinder token,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            int configChanges, boolean notResumed, Configuration config,
            Configuration overrideConfig, boolean fromServer) {
            Configuration overrideConfig, boolean fromServer, boolean preserveWindow) {
        ActivityClientRecord target = null;

        synchronized (mResourcesManager) {
@@ -4020,6 +4047,7 @@ public final class ActivityThread {
                target.token = token;
                target.pendingResults = pendingResults;
                target.pendingIntents = pendingNewIntents;
                target.mPreserveWindow = preserveWindow;
                if (!fromServer) {
                    ActivityClientRecord existing = mActivities.get(token);
                    if (existing != null) {
@@ -4120,6 +4148,7 @@ public final class ActivityThread {

        r.activity.mConfigChangeFlags |= configChanges;
        r.onlyLocalRequest = tmp.onlyLocalRequest;
        r.mPreserveWindow = tmp.mPreserveWindow;
        Intent currentIntent = r.activity.mIntent;

        r.activity.mChangingConfigurations = true;
+19 −16
Original line number Diff line number Diff line
@@ -179,7 +179,9 @@ public abstract class ApplicationThreadNative extends Binder
            if (data.readInt() != 0) {
                overrideConfig = Configuration.CREATOR.createFromParcel(data);
            }
            scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig);
            boolean preserveWindows = data.readInt() == 1;
            scheduleRelaunchActivity(b, ri, pi, configChanges, notResumed, config, overrideConfig,
                    preserveWindows);
            return true;
        }

@@ -856,7 +858,7 @@ class ApplicationThreadProxy implements IApplicationThread {
    public final void scheduleRelaunchActivity(IBinder token,
            List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
            int configChanges, boolean notResumed, Configuration config,
            Configuration overrideConfig) throws RemoteException {
            Configuration overrideConfig, boolean preserveWindow) throws RemoteException {
        Parcel data = Parcel.obtain();
        data.writeInterfaceToken(IApplicationThread.descriptor);
        data.writeStrongBinder(token);
@@ -871,6 +873,7 @@ class ApplicationThreadProxy implements IApplicationThread {
        } else {
            data.writeInt(0);
        }
        data.writeInt(preserveWindow ? 1 : 0);
        mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
                IBinder.FLAG_ONEWAY);
        data.recycle();
Loading