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);
taskRect.top += maxMove;
taskRect.bottom += maxMove;
}
- taskResize(taskId, taskRect, delay_ms);
+ taskResize(taskId, taskRect, delay_ms, false);
}
} else {
while (maxToTravel < 0
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.
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);
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);
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);
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);
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);
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);
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);
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);
}
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 */);
}
/**
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);
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();
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;
}
}
@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();
int pendingConfigChanges;
boolean onlyLocalRequest;
- View mPendingRemoveWindow;
+ Window mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;
+ boolean mPreserveWindow;
ActivityClientRecord() {
parent = null;
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) {
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;
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");
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);
}
}
// 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.
// 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 */);
}
}
}
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) {
}
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);
}
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) {
target.token = token;
target.pendingResults = pendingResults;
target.pendingIntents = pendingNewIntents;
+ target.mPreserveWindow = preserveWindow;
if (!fromServer) {
ActivityClientRecord existing = mActivities.get(token);
if (existing != null) {
r.activity.mConfigChangeFlags |= configChanges;
r.onlyLocalRequest = tmp.onlyLocalRequest;
+ r.mPreserveWindow = tmp.mPreserveWindow;
Intent currentIntent = r.activity.mIntent;
r.activity.mChangingConfigurations = true;
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 {
scheduleStopActivity(b, show, configChanges);
return true;
}
-
+
case SCHEDULE_WINDOW_VISIBILITY_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
scheduleResumeActivity(b, procState, isForward, resumeArgs);
return true;
}
-
+
case SCHEDULE_SEND_RESULT_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
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;
}
scheduleDestroyActivity(b, finishing, configChanges);
return true;
}
-
+
case SCHEDULE_RECEIVER_TRANSACTION:
{
data.enforceInterface(IApplicationThread.descriptor);
}
return true;
}
-
+
case DUMP_PROVIDER_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
ParcelFileDescriptor fd = data.readFileDescriptor();
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();
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);
} else {
data.writeInt(0);
}
+ data.writeInt(preserveWindow ? 1 : 0);
mRemote.transact(SCHEDULE_RELAUNCH_ACTIVITY_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();
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 {
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleCreateService(IBinder token, ServiceInfo info,
CompatibilityInfo compatInfo, int processState) throws RemoteException {
Parcel data = Parcel.obtain();
IBinder.FLAG_ONEWAY);
data.recycle();
}
-
+
public final void scheduleExit() throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
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();
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;
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;
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;
}
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.
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.
*
*/
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;
+ }
}
// 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.
private Drawable mBackgroundDrawable;
+ private boolean mLoadEleveation = true;
private float mElevation;
/** Whether window content should be clipped to the background outline. */
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);
}
}
+ public void clearContentView() {
+ if (mNonClientDecorView.getChildCount() > 1) {
+ mNonClientDecorView.removeViewAt(1);
+ }
+ }
+
private void transitionTo(Scene scene) {
if (mContentScene == null) {
scene.enter();
}
@Override
+ public float getElevation() {
+ return mElevation;
+ }
+
+ @Override
public final void setClipToOutline(boolean clipToOutline) {
mClipToOutline = clipToOutline;
if (mDecor != null) {
@Override
public final View getDecorView() {
- if (mDecor == null) {
+ if (mDecor == null || mForceDecorInstall) {
installDecor();
}
return mDecor;
+ 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);
}
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));
// 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;
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;
}
}
private void installDecor() {
+ mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
// 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;
+ }
}
}
@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();
Slog.w(TAG, "resizeTask: taskId=" + taskId + " not found");
return;
}
- mStackSupervisor.resizeTaskLocked(task, bounds);
+ mStackSupervisor.resizeTaskLocked(task, bounds, resizedByUser);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
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);
// 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) {
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);
* 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);
// "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;
}
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) {
}
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);
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.
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);
}
}
- 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;
&& 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");
}
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 */);
+ }
}
}
}
// 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
}
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);
notifyMoveLocked(newX, newY);
}
try {
- mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds);
+ mService.mActivityManager.resizeTask(mTaskId, mWindowDragBounds,
+ true /* resizedByUser */);
} catch(RemoteException e) {}
} break;
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);