OSDN Git Service

Preserve window during resize triggered relaunches.
authorFilip Gruszczynski <gruszczy@google.com>
Fri, 11 Sep 2015 01:28:48 +0000 (18:28 -0700)
committerFilip Gruszczynski <gruszczy@google.com>
Mon, 14 Sep 2015 22:59:32 +0000 (15:59 -0700)
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

16 files changed:
cmds/am/src/com/android/commands/am/Am.java
core/java/android/app/Activity.java
core/java/android/app/ActivityManagerNative.java
core/java/android/app/ActivityThread.java
core/java/android/app/ApplicationThreadNative.java
core/java/android/app/IActivityManager.java
core/java/android/app/IApplicationThread.java
core/java/android/app/Instrumentation.java
core/java/android/view/Window.java
core/java/com/android/internal/policy/PhoneWindow.java
services/core/java/com/android/server/am/ActivityManagerService.java
services/core/java/com/android/server/am/ActivityStack.java
services/core/java/com/android/server/am/ActivityStackSupervisor.java
services/core/java/com/android/server/am/CompatModePackages.java
services/core/java/com/android/server/wm/TaskPositioner.java
services/core/java/com/android/server/wm/WallpaperController.java

index 9709299..9185d7a 100644 (file)
@@ -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);
     }
index f7dcf02..4997dc7 100644 (file)
@@ -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,11 +6319,15 @@ public class Activity extends ContextThemeWrapper
     final void performRestart() {
         mFragments.noteStateNotSaved();
 
+        if (mToken != null && mParent == null) {
+            // 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;
-            if (mToken != null && mParent == null) {
-                WindowManagerGlobal.getInstance().setStoppedState(mToken, false);
-            }
 
             synchronized (mManagedCursors) {
                 final int N = mManagedCursors.size();
index d79716c..da6fc59 100644 (file)
@@ -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();
index 67dee7f..4b8efab 100644 (file)
@@ -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;
index f164a0a..bead625 100644 (file)
@@ -63,14 +63,14 @@ public abstract class ApplicationThreadNative extends Binder
         if (in != null) {
             return in;
         }
-        
+
         return new ApplicationThreadProxy(obj);
     }
-    
+
     public ApplicationThreadNative() {
         attachInterface(this, descriptor);
     }
-    
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -96,7 +96,7 @@ public abstract class ApplicationThreadNative extends Binder
             scheduleStopActivity(b, show, configChanges);
             return true;
         }
-        
+
         case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -125,7 +125,7 @@ public abstract class ApplicationThreadNative extends Binder
             scheduleResumeActivity(b, procState, isForward, resumeArgs);
             return true;
         }
-        
+
         case SCHEDULE_SEND_RESULT_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -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;
         }
 
@@ -201,7 +203,7 @@ public abstract class ApplicationThreadNative extends Binder
             scheduleDestroyActivity(b, finishing, configChanges);
             return true;
         }
-        
+
         case SCHEDULE_RECEIVER_TRANSACTION:
         {
             data.enforceInterface(IApplicationThread.descriptor);
@@ -371,7 +373,7 @@ public abstract class ApplicationThreadNative extends Binder
             }
             return true;
         }
-        
+
         case DUMP_PROVIDER_TRANSACTION: {
             data.enforceInterface(IApplicationThread.descriptor);
             ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -731,15 +733,15 @@ public abstract class ApplicationThreadNative extends Binder
 
 class ApplicationThreadProxy implements IApplicationThread {
     private final IBinder mRemote;
-    
+
     public ApplicationThreadProxy(IBinder remote) {
         mRemote = remote;
     }
-    
+
     public final IBinder asBinder() {
         return mRemote;
     }
-    
+
     public final void schedulePauseActivity(IBinder token, boolean finished,
             boolean userLeaving, int configChanges, boolean dontReport) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -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();
@@ -898,7 +901,7 @@ class ApplicationThreadProxy implements IApplicationThread {
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
-    
+
     public final void scheduleReceiver(Intent intent, ActivityInfo info,
             CompatibilityInfo compatInfo, int resultCode, String resultData,
             Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
@@ -940,7 +943,7 @@ class ApplicationThreadProxy implements IApplicationThread {
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
-    
+
     public final void scheduleCreateService(IBinder token, ServiceInfo info,
             CompatibilityInfo compatInfo, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -1055,7 +1058,7 @@ class ApplicationThreadProxy implements IApplicationThread {
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
-    
+
     public final void scheduleExit() throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1128,7 +1131,7 @@ class ApplicationThreadProxy implements IApplicationThread {
         mRemote.transact(DUMP_SERVICE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
-    
+
     public void dumpProvider(FileDescriptor fd, IBinder token, String[] args)
             throws RemoteException {
         Parcel data = Parcel.obtain();
index 3b30d71..7bd832b 100644 (file)
@@ -491,7 +491,7 @@ public interface IActivityManager extends IInterface {
     public void setTaskDescription(IBinder token, ActivityManager.TaskDescription values)
             throws RemoteException;
     public void setTaskResizeable(int taskId, boolean resizeable) throws RemoteException;
-    public void resizeTask(int taskId, Rect bounds) throws RemoteException;
+    public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) throws RemoteException;
 
     public Rect getTaskBounds(int taskId) throws RemoteException;
     public Bitmap getTaskDescriptionIcon(String filename) throws RemoteException;
index dc8f53d..2d78e19 100644 (file)
@@ -65,7 +65,8 @@ public interface IApplicationThread extends IInterface {
             boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) throws RemoteException;
     void scheduleRelaunchActivity(IBinder token, List<ResultInfo> pendingResults,
             List<ReferrerIntent> pendingNewIntents, int configChanges, boolean notResumed,
-            Configuration config, Configuration overrideConfig) throws RemoteException;
+            Configuration config, Configuration overrideConfig, boolean preserveWindow)
+            throws RemoteException;
     void scheduleNewIntent(List<ReferrerIntent> intent, IBinder token) throws RemoteException;
     void scheduleDestroyActivity(IBinder token, boolean finished,
             int configChanges) throws RemoteException;
index dee8d21..7184337 100644 (file)
@@ -1044,7 +1044,7 @@ public class Instrumentation {
         activity.attach(context, aThread, this, token, 0, application, intent,
                 info, title, parent, id,
                 (Activity.NonConfigurationInstances)lastNonConfigurationInstance,
-                new Configuration(), null, null);
+                new Configuration(), null, null, null);
         return activity;
     }
 
index b146a51..0e7089f 100644 (file)
@@ -1175,6 +1175,13 @@ public abstract class Window {
     public abstract void addContentView(View view, ViewGroup.LayoutParams params);
 
     /**
+     * Remove the view that was used as the screen content.
+     *
+     * @hide
+     */
+    public abstract void clearContentView();
+
+    /**
      * Return the view in this Window that currently has focus, or null if
      * there are none.  Note that this does not look in any containing
      * Window.
@@ -1239,6 +1246,15 @@ public abstract class Window {
     public void setElevation(float elevation) {}
 
     /**
+     * Gets the window elevation.
+     *
+     * @hide
+     */
+    public float getElevation() {
+        return 0.0f;
+    }
+
+    /**
      * Sets whether window content should be clipped to the outline of the
      * window background.
      *
@@ -1991,5 +2007,13 @@ public abstract class Window {
      */
     public abstract void setNavigationBarColor(@ColorInt int color);
 
-
+    /**
+     * Get information whether the activity has non client decoration view. These views are used in
+     * the multi window environment, to provide dragging handle and maximize/close buttons.
+     *
+     * @hide
+     */
+    public boolean hasNonClientDecorView() {
+        return false;
+    }
 }
index 7f01841..3353d16 100644 (file)
@@ -170,6 +170,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
     // This is the top-level view of the window, containing the window decor.
     private DecorView mDecor;
 
+    // When we reuse decor views, we need to recreate the content root. This happens when the decor
+    // view is requested, so we need to force the recreating without introducing an infinite loop.
+    private boolean mForceDecorInstall = false;
+
     // This is the non client decor view for the window, containing the caption and window control
     // buttons. The visibility of this decor depends on the workspace and the window type.
     // If the window type does not require such a view, this member might be null.
@@ -248,6 +252,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
 
     private Drawable mBackgroundDrawable;
 
+    private boolean mLoadEleveation = true;
     private float mElevation;
 
     /** Whether window content should be clipped to the background outline. */
@@ -323,6 +328,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
         mLayoutInflater = LayoutInflater.from(context);
     }
 
+    public PhoneWindow(Context context, Window preservedWindow) {
+        this(context);
+        if (preservedWindow != null) {
+            mDecor = (DecorView) preservedWindow.getDecorView();
+            mElevation = preservedWindow.getElevation();
+            mLoadEleveation = false;
+            mForceDecorInstall = true;
+        }
+    }
+
     @Override
     public final void setContainer(Window container) {
         super.setContainer(container);
@@ -463,6 +478,12 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
         }
     }
 
+    public void clearContentView() {
+        if (mNonClientDecorView.getChildCount() > 1) {
+            mNonClientDecorView.removeViewAt(1);
+        }
+    }
+
     private void transitionTo(Scene scene) {
         if (mContentScene == null) {
             scene.enter();
@@ -1396,6 +1417,11 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
     }
 
     @Override
+    public float getElevation() {
+        return mElevation;
+    }
+
+    @Override
     public final void setClipToOutline(boolean clipToOutline) {
         mClipToOutline = clipToOutline;
         if (mDecor != null) {
@@ -1992,7 +2018,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
 
     @Override
     public final View getDecorView() {
-        if (mDecor == null) {
+        if (mDecor == null || mForceDecorInstall) {
             installDecor();
         }
         return mDecor;
@@ -3960,7 +3986,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                             + Integer.toHexString(mFrameResource));
                 }
             }
-            mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+            if (mLoadEleveation) {
+                mElevation = a.getDimension(R.styleable.Window_windowElevation, 0);
+            }
             mClipToOutline = a.getBoolean(R.styleable.Window_windowClipToOutline, false);
             mTextColor = a.getColor(R.styleable.Window_textColor, Color.TRANSPARENT);
         }
@@ -4032,8 +4060,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
         mNonClientDecorView = createNonClientDecorView();
         View in = mLayoutInflater.inflate(layoutResource, null);
         if (mNonClientDecorView != null) {
-            decor.addView(mNonClientDecorView,
-                    new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+            if (mNonClientDecorView.getParent() == null) {
+                decor.addView(mNonClientDecorView,
+                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
+            }
             mNonClientDecorView.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
         } else {
             decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
@@ -4096,6 +4126,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
     // Free floating overlapping windows require a non client decor with a caption and shadow..
     private NonClientDecorView createNonClientDecorView() {
         NonClientDecorView nonClientDecorView = null;
+        for (int i = mDecor.getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
+            View view = mDecor.getChildAt(i);
+            if (view instanceof NonClientDecorView) {
+                // The decor was most likely saved from a relaunch - so reuse it.
+                nonClientDecorView = (NonClientDecorView) view;
+                mDecor.removeViewAt(i);
+            }
+        }
         final WindowManager.LayoutParams attrs = getAttributes();
         boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
                 attrs.type == TYPE_APPLICATION;
@@ -4106,21 +4144,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
                 mWorkspaceId < FIRST_DYNAMIC_STACK_ID) {
             // Dependent on the brightness of the used title we either use the
             // dark or the light button frame.
-            TypedValue value = new TypedValue();
-            getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
-            if (Color.luminance(value.data) < 0.5) {
-                nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
-                        R.layout.non_client_decor_dark, null);
-            } else {
-                nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
-                        R.layout.non_client_decor_light, null);
+            if (nonClientDecorView == null) {
+                TypedValue value = new TypedValue();
+                getContext().getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
+                if (Color.luminance(value.data) < 0.5) {
+                    nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+                            R.layout.non_client_decor_dark, null);
+                } else {
+                    nonClientDecorView = (NonClientDecorView) mLayoutInflater.inflate(
+                            R.layout.non_client_decor_light, null);
+                }
             }
             nonClientDecorView.setPhoneWindow(this, hasNonClientDecor(mWorkspaceId),
                     nonClientDecorHasShadow(mWorkspaceId));
         }
         // Tell the decor if it has a visible non client decor.
-        mDecor.enableNonClientDecor(nonClientDecorView != null &&
-                hasNonClientDecor(mWorkspaceId));
+        mDecor.enableNonClientDecor(nonClientDecorView != null && hasNonClientDecor(mWorkspaceId));
 
         return nonClientDecorView;
     }
@@ -4131,6 +4170,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
     }
 
     private void installDecor() {
+        mForceDecorInstall = false;
         if (mDecor == null) {
             mDecor = generateDecor(-1);
             mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
@@ -5307,4 +5347,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
         // TODO(skuhne): Add side by side mode here to add a decor.
         return workspaceId == FREEFORM_WORKSPACE_STACK_ID;
     }
+
+    @Override
+    public boolean hasNonClientDecorView() {
+        return mNonClientDecorView != null;
+    }
 }
index dc6f3d6..b21e902 100644 (file)
@@ -8650,7 +8650,7 @@ public final class ActivityManagerService extends ActivityManagerNative
     }
 
     @Override
-    public void resizeTask(int taskId, Rect bounds) {
+    public void resizeTask(int taskId, Rect bounds, boolean resizedByUser) {
         enforceCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS,
                 "resizeTask()");
         long ident = Binder.clearCallingIdentity();
@@ -8661,7 +8661,7 @@ public final class ActivityManagerService extends ActivityManagerNative
                     Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
                     return;
                 }
-                mStackSupervisor.resizeTaskLocked(task, bounds);
+                mStackSupervisor.resizeTaskLocked(task, bounds, resizedByUser);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -17660,7 +17660,7 @@ public final class ActivityManagerService extends ActivityManagerNative
             }
 
             if (starting != null) {
-                kept = mainStack.ensureActivityConfigurationLocked(starting, changes);
+                kept = mainStack.ensureActivityConfigurationLocked(starting, changes, false);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
                 mStackSupervisor.ensureActivitiesVisibleLocked(starting, changes);
index a8a0073..3351226 100644 (file)
@@ -1427,7 +1427,7 @@ final class ActivityStack {
                     // First: if this is not the current activity being started, make
                     // sure it matches the current configuration.
                     if (r != starting) {
-                        ensureActivityConfigurationLocked(r, 0);
+                        ensureActivityConfigurationLocked(r, 0, false);
                     }
 
                     if (r.app == null || r.app.thread == null) {
@@ -3500,7 +3500,7 @@ final class ActivityStack {
     final boolean destroyActivityLocked(ActivityRecord r, boolean removeFromApp, String reason) {
         if (DEBUG_SWITCH || DEBUG_CLEANUP) Slog.v(TAG_SWITCH,
                 "Removing activity from " + reason + ": token=" + r
-                + ", app=" + (r.app != null ? r.app.processName : "(null)"));
+                        + ", app=" + (r.app != null ? r.app.processName : "(null)"));
         EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
                 r.userId, System.identityHashCode(r),
                 r.task.taskId, r.shortComponentName, reason);
@@ -3977,7 +3977,8 @@ final class ActivityStack {
      * for whatever reason.  Ensures the HistoryRecord is updated with the
      * correct configuration and all other bookkeeping is handled.
      */
-    final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges) {
+    final boolean ensureActivityConfigurationLocked(ActivityRecord r, int globalChanges,
+            boolean preserveWindow) {
         if (mConfigWillChange) {
             if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                     "Skipping config check (will change): " + r);
@@ -4062,12 +4063,14 @@ final class ActivityStack {
                 // "restart!".
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching resumed " + r);
-                relaunchActivityLocked(r, r.configChangeFlags, true);
+                relaunchActivityLocked(r, r.configChangeFlags, true,
+                        preserveWindow && isResizeOnlyChange(changes));
                 r.configChangeFlags = 0;
             } else {
                 if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG_CONFIGURATION,
                         "Config is relaunching non-resumed " + r);
-                relaunchActivityLocked(r, r.configChangeFlags, false);
+                relaunchActivityLocked(r, r.configChangeFlags, false,
+                        preserveWindow && isResizeOnlyChange(changes));
                 r.configChangeFlags = 0;
             }
 
@@ -4160,7 +4163,13 @@ final class ActivityStack {
         return taskChanges;
     }
 
-    private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume) {
+    private static boolean isResizeOnlyChange(int change) {
+        return (change & ~(ActivityInfo.CONFIG_SCREEN_SIZE
+                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_ORIENTATION)) == 0;
+    }
+
+    private boolean relaunchActivityLocked(ActivityRecord r, int changes, boolean andResume,
+            boolean preserveWindow) {
         List<ResultInfo> results = null;
         List<ReferrerIntent> newIntents = null;
         if (andResume) {
@@ -4169,7 +4178,7 @@ final class ActivityStack {
         }
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH,
                 "Relaunching: " + r + " with results=" + results + " newIntents=" + newIntents
-                + " andResume=" + andResume);
+                + " andResume=" + andResume + " preserveWindow=" + preserveWindow);
         EventLog.writeEvent(andResume ? EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY
                 : EventLogTags.AM_RELAUNCH_ACTIVITY, r.userId, System.identityHashCode(r),
                 r.task.taskId, r.shortComponentName);
@@ -4184,7 +4193,7 @@ final class ActivityStack {
             r.forceNewConfig = false;
             r.app.thread.scheduleRelaunchActivity(r.appToken, results, newIntents, changes,
                     !andResume, new Configuration(mService.mConfiguration),
-                    new Configuration(r.task.mOverrideConfig));
+                    new Configuration(r.task.mOverrideConfig), preserveWindow);
             // Note: don't need to call pauseIfSleepingLocked() here, because
             // the caller will only pass in 'andResume' if this activity is
             // currently resumed, which implies we aren't sleeping.
index 8ab4ae5..d3cea8d 100644 (file)
@@ -3045,7 +3045,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
         stack.setBounds(bounds);
 
         if (r != null) {
-            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0);
+            final boolean updated = stack.ensureActivityConfigurationLocked(r, 0, false);
             // And we need to make sure at this point that all other activities
             // are made visible with the correct configuration.
             ensureActivitiesVisibleLocked(r, 0);
@@ -3055,7 +3055,7 @@ public final class ActivityStackSupervisor implements DisplayListener {
         }
     }
 
-    void resizeTaskLocked(TaskRecord task, Rect bounds) {
+    void resizeTaskLocked(TaskRecord task, Rect bounds, boolean resizedByUser) {
         if (!task.mResizeable) {
             Slog.w(TAG, "resizeTask: task " + task + " not resizeable.");
             return;
@@ -3088,7 +3088,8 @@ public final class ActivityStackSupervisor implements DisplayListener {
                 && stackId != FREEFORM_WORKSPACE_STACK_ID && stackId != DOCKED_STACK_ID) {
             stackId = FREEFORM_WORKSPACE_STACK_ID;
         }
-        if (stackId != task.stack.mStackId) {
+        final boolean changedStacks = stackId != task.stack.mStackId;
+        if (changedStacks) {
             moveTaskToStackUncheckedLocked(task, stackId, ON_TOP, !FORCE_FOCUS, "resizeTask");
         }
 
@@ -3101,20 +3102,22 @@ public final class ActivityStackSupervisor implements DisplayListener {
             ActivityRecord r = task.topRunningActivityLocked(null);
             if (r != null) {
                 final ActivityStack stack = task.stack;
-                kept = stack.ensureActivityConfigurationLocked(r, 0);
+                final boolean preserveWindow = resizedByUser && !changedStacks;
+                kept = stack.ensureActivityConfigurationLocked(r, 0, preserveWindow);
                 // All other activities must be made visible with their correct configuration.
                 ensureActivitiesVisibleLocked(r, 0);
                 if (!kept) {
                     resumeTopActivitiesLocked(stack, null, null);
-                    // We are about to relaunch the activity because its configuration changed due
-                    // to size change. The activity will first remove the old window and then add a
-                    // new one. This call will tell window manager about this, so it can preserve
-                    // the old window until the new one is drawn. This prevents having a gap between
-                    // the removal and addition, in which no window is visible. If we also changed
-                    // the stack to the fullscreen stack, i.e. maximized the window, we will animate
-                    // the transition.
-                    mWindowManager.setReplacingWindow(r.appToken,
-                            stackId == FULLSCREEN_WORKSPACE_STACK_ID /* animate */);
+                    if (changedStacks && stackId == FULLSCREEN_WORKSPACE_STACK_ID) {
+                        // We are about to relaunch the activity because its configuration changed
+                        // due to being maximized, i.e. size change. The activity will first
+                        // remove the old window and then add a new one. This call will tell window
+                        // manager about this, so it can preserve the old window until the new
+                        // one is drawn. This prevents having a gap between the removal and
+                        // addition, in which no window is visible. We also want the entrace of the
+                        // new window to be properly animated.
+                        mWindowManager.setReplacingWindow(r.appToken, true /* animate */);
+                    }
                 }
             }
         }
@@ -3240,12 +3243,12 @@ public final class ActivityStackSupervisor implements DisplayListener {
 
         // Make sure the task has the appropriate bounds/size for the stack it is in.
         if (stackId == FULLSCREEN_WORKSPACE_STACK_ID && task.mBounds != null) {
-            resizeTaskLocked(task, stack.mBounds);
+            resizeTaskLocked(task, stack.mBounds, false);
         } else if (stackId == FREEFORM_WORKSPACE_STACK_ID
                 && task.mBounds == null && task.mLastNonFullscreenBounds != null) {
-            resizeTaskLocked(task, task.mLastNonFullscreenBounds);
+            resizeTaskLocked(task, task.mLastNonFullscreenBounds, false);
         } else if (stackId == DOCKED_STACK_ID) {
-            resizeTaskLocked(task, stack.mBounds);
+            resizeTaskLocked(task, stack.mBounds, false);
         }
 
         // The task might have already been running and its visibility needs to be synchronized with
index 814e8b4..424ceb1 100644 (file)
@@ -344,7 +344,7 @@ public final class CompatModePackages {
             }
 
             if (starting != null) {
-                stack.ensureActivityConfigurationLocked(starting, 0);
+                stack.ensureActivityConfigurationLocked(starting, 0, false);
                 // And we need to make sure at this point that all other activities
                 // are made visible with the correct configuration.
                 stack.ensureActivitiesVisibleLocked(starting, 0);
index 876e9aa..17936a6 100644 (file)
@@ -131,7 +131,8 @@ class TaskPositioner implements DimLayer.DimLayerUser {
                             notifyMoveLocked(newX, newY);
                         }
                         try {
-                            mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
+                            mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds,
+                                    true /* resizedByUser */);
                         } catch(RemoteException e) {}
                     } break;
 
index fc23fd1..1a946b2 100644 (file)
@@ -736,7 +736,8 @@ class WallpaperController {
                         insertionIndex = windows.indexOf(wallpaperTarget);
                     }
                 }
-                if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG,
+                if (DEBUG_WALLPAPER_LIGHT || DEBUG_WINDOW_MOVEMENT
+                        || (DEBUG_ADD_REMOVE && oldIndex != insertionIndex)) Slog.v(TAG,
                         "Moving wallpaper " + wallpaper
                         + " from " + oldIndex + " to " + insertionIndex);