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
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 9709299..9185d7a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -2266,12 +2266,12 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f7dcf02..4997dc7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4879,7 +4879,8 @@
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 @@
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 @@
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();
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index d79716c..da6fc59 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2452,8 +2452,9 @@
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 @@
}
@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();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 67dee7f..4b8efab 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -315,8 +315,9 @@
int pendingConfigChanges;
boolean onlyLocalRequest;
- View mPendingRemoveWindow;
+ Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
+ boolean mPreserveWindow;
ActivityClientRecord() {
parent = null;
@@ -670,9 +671,9 @@
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 @@
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 @@
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 @@
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 @@
}
// 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 @@
// 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 @@
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 @@
}
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 @@
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 @@
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 @@
r.activity.mConfigChangeFlags |= configChanges;
r.onlyLocalRequest = tmp.onlyLocalRequest;
+ r.mPreserveWindow = tmp.mPreserveWindow;
Intent currentIntent = r.activity.mIntent;
r.activity.mChangingConfigurations = true;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index f164a0a..bead625 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -63,14 +63,14 @@
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 @@
scheduleStopActivity(b, show, configChanges);
return true;
}
-
+
case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -125,7 +125,7 @@
scheduleResumeActivity(b, procState, isForward, resumeArgs);
return true;
}
-
+
case SCHEDULE_SEND_RESULT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -179,7 +179,9 @@
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 @@
scheduleDestroyActivity(b, finishing, configChanges);
return true;
}
-
+
case SCHEDULE_RECEIVER_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
@@ -371,7 +373,7 @@
}
return true;
}
-
+
case DUMP_PROVIDER_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
@@ -731,15 +733,15 @@
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 @@
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 @@
} 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 @@
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 @@
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 @@
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleExit() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1128,7 +1131,7 @@
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();
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3b30d71..7bd832b 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -491,7 +491,7 @@
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;
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index dc8f53d..2d78e19 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -65,7 +65,8 @@
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;
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index dee8d21..7184337 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -1044,7 +1044,7 @@
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;
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index b146a51..0e7089f 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -1175,6 +1175,13 @@
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 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 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;
+ }
}
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 7f01841..3353d16 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -170,6 +170,10 @@
// 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 @@
private Drawable mBackgroundDrawable;
+ private boolean mLoadEleveation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
@@ -323,6 +328,16 @@
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 void clearContentView() {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ }
+
private void transitionTo(Scene scene) {
if (mContentScene == null) {
scene.enter();
@@ -1396,6 +1417,11 @@
}
@Override
+ public float getElevation() {
+ return mElevation;
+ }
+
+ @Override
public final void setClipToOutline(boolean clipToOutline) {
mClipToOutline = clipToOutline;
if (mDecor != null) {
@@ -1992,7 +2018,7 @@
@Override
public final View getDecorView() {
- if (mDecor == null) {
+ if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
@@ -3960,7 +3986,9 @@
+ 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 @@
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 @@
// 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 @@
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 @@
}
private void installDecor() {
+ mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
@@ -5307,4 +5347,9 @@
// 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;
+ }
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index dc6f3d6..b21e9027 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8650,7 +8650,7 @@
}
@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 @@
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 @@
}
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);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index a8a0073..3351226 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -1427,7 +1427,7 @@
// 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 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 @@
* 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 @@
// "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 @@
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 @@
}
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 @@
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.
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 8ab4ae5..d3cea8d 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -3045,7 +3045,7 @@
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 @@
}
}
- 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 @@
&& 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 @@
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 @@
// 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
diff --git a/services/core/java/com/android/server/am/CompatModePackages.java b/services/core/java/com/android/server/am/CompatModePackages.java
index 814e8b4..424ceb1 100644
--- a/services/core/java/com/android/server/am/CompatModePackages.java
+++ b/services/core/java/com/android/server/am/CompatModePackages.java
@@ -344,7 +344,7 @@
}
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);
diff --git a/services/core/java/com/android/server/wm/TaskPositioner.java b/services/core/java/com/android/server/wm/TaskPositioner.java
index 876e9aa..17936a6 100644
--- a/services/core/java/com/android/server/wm/TaskPositioner.java
+++ b/services/core/java/com/android/server/wm/TaskPositioner.java
@@ -131,7 +131,8 @@
notifyMoveLocked(newX, newY);
}
try {
- mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
+ mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds,
+ true /* resizedByUser */);
} catch(RemoteException e) {}
} break;
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index fc23fd1..1a946b2 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -736,7 +736,8 @@
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);