Add WindowManager side timeout for toasts.

If the thread a toast is shown on is shut down,
the attempt to post the hide message to it's handler
will fail and it will never request removal. If this was
only some application background thread we will also not
receive a death notification. It seems best to use a timeout
to ensure we don't need the clients cooperation, espescially
as toasts can keep the screen on.

Bug: 21693547
Change-Id: I1d6e54ded5b9e2050daedc4d263e2e21fbe69862
This commit is contained in:
Robert Carr
2016-04-10 16:33:08 -07:00
parent fd580f7d1a
commit 70f0d229c0
3 changed files with 41 additions and 2 deletions

View File

@@ -1726,6 +1726,16 @@ public interface WindowManager extends ViewManager {
*/
public CharSequence accessibilityTitle;
/**
* Sets a timeout in milliseconds before which the window will be removed
* by the window manager. Useful for transient notifications like toasts
* so we don't have to rely on client cooperation to ensure the window
* is removed. Must be specified at window creation time.
*
* @hide
*/
public long removeTimeoutMilliseconds = -1;
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;
@@ -1847,6 +1857,7 @@ public interface WindowManager extends ViewManager {
out.writeInt(needsMenuKey);
out.writeInt(accessibilityIdOfAnchor);
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
out.writeLong(removeTimeoutMilliseconds);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1900,6 +1911,7 @@ public interface WindowManager extends ViewManager {
needsMenuKey = in.readInt();
accessibilityIdOfAnchor = in.readInt();
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
removeTimeoutMilliseconds = in.readLong();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2120,6 +2132,9 @@ public interface WindowManager extends ViewManager {
changes |= ACCESSIBILITY_TITLE_CHANGED;
}
// This can't change, it's only set at window creation time.
removeTimeoutMilliseconds = o.removeTimeoutMilliseconds;
return changes;
}

View File

@@ -163,6 +163,7 @@ public class Toast {
*/
public void setDuration(@Duration int duration) {
mDuration = duration;
mTN.mDuration = duration;
}
/**
@@ -342,7 +343,7 @@ public class Toast {
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
final Handler mHandler = new Handler();
final Handler mHandler = new Handler();
int mGravity;
int mX, mY;
@@ -352,9 +353,13 @@ public class Toast {
View mView;
View mNextView;
int mDuration;
WindowManager mWM;
static final long SHORT_DURATION_TIMEOUT = 5000;
static final long LONG_DURATION_TIMEOUT = 1000;
TN() {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
@@ -417,6 +422,8 @@ public class Toast {
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.removeTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);

View File

@@ -2107,6 +2107,11 @@ public class WindowManagerService extends IWindowManager.Stub
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;
}
if (attrs.removeTimeoutMilliseconds > 0) {
mH.sendMessageDelayed(
mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),
attrs.removeTimeoutMilliseconds);
}
}
if (reportNewConfig) {
@@ -7782,8 +7787,8 @@ public class WindowManagerService extends IWindowManager.Stub
public static final int NOTIFY_APP_TRANSITION_CANCELLED = 48;
public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
public static final int UPDATE_ANIMATION_SCALE = 51;
public static final int WINDOW_REMOVE_TIMEOUT = 52;
/**
* Used to denote that an integer field in a message will not be used.
@@ -8396,6 +8401,18 @@ public class WindowManagerService extends IWindowManager.Stub
mAmInternal.notifyStartingWindowDrawn();
}
break;
case WINDOW_REMOVE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized(mWindowMap) {
// It's counterintuitive that we check that "mWindowRemovalAllowed"
// is false. But in fact if it's true, it means a remove has already
// been requested and we better just not do anything.
if (!window.mRemoved && !window.mWindowRemovalAllowed) {
removeWindowLocked(window);
}
}
}
break;
}
if (DEBUG_WINDOW_TRACE) {
Slog.v(TAG_WM, "handleMessage: exit");