Merge "Add WindowManager side timeout for toasts." into nyc-dev
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a3a40eb..89e146b 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1726,6 +1726,16 @@
          */
         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;
@@ -1846,6 +1856,7 @@
             out.writeInt(needsMenuKey);
             out.writeInt(accessibilityIdOfAnchor);
             TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
+            out.writeLong(removeTimeoutMilliseconds);
         }
 
         public static final Parcelable.Creator<LayoutParams> CREATOR
@@ -1899,6 +1910,7 @@
             needsMenuKey = in.readInt();
             accessibilityIdOfAnchor = in.readInt();
             accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            removeTimeoutMilliseconds = in.readLong();
         }
 
         @SuppressWarnings({"PointlessBitwiseExpression"})
@@ -2119,6 +2131,9 @@
                 changes |= ACCESSIBILITY_TITLE_CHANGED;
             }
 
+            // This can't change, it's only set at window creation time.
+            removeTimeoutMilliseconds = o.removeTimeoutMilliseconds;
+
             return changes;
         }
 
diff --git a/core/java/android/widget/Toast.java b/core/java/android/widget/Toast.java
index 207f675..7762675 100644
--- a/core/java/android/widget/Toast.java
+++ b/core/java/android/widget/Toast.java
@@ -163,6 +163,7 @@
      */
     public void setDuration(@Duration int duration) {
         mDuration = duration;
+        mTN.mDuration = duration;
     }
 
     /**
@@ -342,7 +343,7 @@
         };
 
         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 @@
 
         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 @@
                 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);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0a0f0f0..57dc97a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2107,6 +2107,11 @@
             if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
                 reportNewConfig = true;
             }
+            if (attrs.removeTimeoutMilliseconds > 0) {
+                mH.sendMessageDelayed(
+                        mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),
+                        attrs.removeTimeoutMilliseconds);
+            }
         }
 
         if (reportNewConfig) {
@@ -7788,8 +7793,8 @@
         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.
@@ -8402,6 +8407,18 @@
                     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");