Merge "Wait for first scan before partitioning is done."
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 24ebb33..223d528 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -215,8 +215,10 @@
     public static final int OP_ASSIST_SCREENSHOT = 50;
     /** @hide Read the phone state. */
     public static final int OP_READ_PHONE_STATE = 51;
+    /** @hide Add voicemail messages to the voicemail content provider. */
+    public static final int OP_ADD_VOICEMAIL = 52;
     /** @hide */
-    public static final int _NUM_OP = 52;
+    public static final int _NUM_OP = 53;
 
     /** Access to coarse location information. */
     public static final String OPSTR_COARSE_LOCATION =
@@ -297,7 +299,8 @@
             OP_WRITE_WALLPAPER,
             OP_ASSIST_STRUCTURE,
             OP_ASSIST_SCREENSHOT,
-            OP_READ_PHONE_STATE
+            OP_READ_PHONE_STATE,
+            OP_ADD_VOICEMAIL
     };
 
     /**
@@ -356,6 +359,7 @@
             null,
             null,
             null,
+            null,
             null
     };
 
@@ -415,7 +419,8 @@
             "WRITE_WALLPAPER",
             "ASSIST_STRUCTURE",
             "ASSIST_SCREENSHOT",
-            "OP_READ_PHONE_STATE"
+            "OP_READ_PHONE_STATE",
+            "ADD_VOICEMAIL"
     };
 
     /**
@@ -474,7 +479,8 @@
             null, // no permission for supporting wallpaper
             null, // no permission for receiving assist structure
             null, // no permission for receiving assist screenshot
-            Manifest.permission.READ_PHONE_STATE
+            Manifest.permission.READ_PHONE_STATE,
+            Manifest.permission.ADD_VOICEMAIL
     };
 
     /**
@@ -534,7 +540,8 @@
             UserManager.DISALLOW_WALLPAPER, // WRITE_WALLPAPER
             null, // ASSIST_STRUCTURE
             null, // ASSIST_SCREENSHOT
-            null  // READ_PHONE_STATE
+            null, // READ_PHONE_STATE
+            null // ADD_VOICEMAIL
     };
 
     /**
@@ -594,6 +601,7 @@
             false, //ASSIST_STRUCTURE
             false, //ASSIST_SCREENSHOT
             false, //READ_PHONE_STATE
+            false  //ADD_VOICEMAIL
     };
 
     /**
@@ -651,6 +659,7 @@
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED,
+            AppOpsManager.MODE_ALLOWED,
             AppOpsManager.MODE_ALLOWED
     };
 
@@ -713,6 +722,7 @@
             false,
             false,
             false,
+            false,
             false
     };
 
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 1e96945..654d6bf 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -108,8 +108,7 @@
     }
 
     /**
-     * Sets the custom listener for invocation of menu items in this floating
-     * toolbar.
+     * Sets the custom listener for invocation of menu items in this floating toolbar.
      */
     public FloatingToolbar setOnMenuItemClickListener(
             MenuItem.OnMenuItemClickListener menuItemClickListener) {
@@ -188,13 +187,28 @@
     }
 
     /**
-     * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
+     * Hides this floating toolbar. This is a no-op if the toolbar is not showing.
+     * Use {@link #isHidden()} to distinguish between a hidden and a dismissed toolbar.
+     */
+    public void hide() {
+        mPopup.hide();
+    }
+
+    /**
+     * Returns {@code true} if this toolbar is currently showing. {@code false} otherwise.
      */
     public boolean isShowing() {
         return mPopup.isShowing();
     }
 
     /**
+     * Returns {@code true} if this toolbar is currently hidden. {@code false} otherwise.
+     */
+    public boolean isHidden() {
+        return mPopup.isHidden();
+    }
+
+    /**
      * Refreshes {@link #mCoordinates} with values based on {@link #mContentRect}.
      */
     private void refreshCoordinates() {
@@ -306,8 +320,9 @@
                     public void onAnimationRepeat(Animation animation) {
                     }
                 };
-        private final AnimatorSet mGrowFadeInFromBottomAnimation;
-        private final AnimatorSet mShrinkFadeOutFromBottomAnimation;
+        private final AnimatorSet mShowAnimation;
+        private final AnimatorSet mDismissAnimation;
+        private final AnimatorSet mHideAnimation;
 
         private final Runnable mOpenOverflow = new Runnable() {
             @Override
@@ -324,7 +339,8 @@
 
         private final Region mTouchableRegion = new Region();
 
-        private boolean mDismissAnimating;
+        private boolean mDismissed = true; // tracks whether this popup is dismissed or dismissing.
+        private boolean mHidden; // tracks whether this popup is hidden or hiding.
 
         private FloatingToolbarOverflowPanel mOverflowPanel;
         private FloatingToolbarMainPanel mMainPanel;
@@ -340,17 +356,24 @@
             mParent = Preconditions.checkNotNull(parent);
             mContentContainer = createContentContainer(parent.getContext());
             mPopupWindow = createPopupWindow(mContentContainer);
-            mGrowFadeInFromBottomAnimation = createGrowFadeInFromBottom(mContentContainer);
-            mShrinkFadeOutFromBottomAnimation = createShrinkFadeOutFromBottomAnimation(
+            mShowAnimation = createGrowFadeInFromBottom(mContentContainer);
+            mDismissAnimation = createShrinkFadeOutFromBottomAnimation(
                     mContentContainer,
                     new AnimatorListenerAdapter() {
                         @Override
                         public void onAnimationEnd(Animator animation) {
                             mPopupWindow.dismiss();
-                            mDismissAnimating = false;
                             setMainPanelAsContent();
                         }
                     });
+            mHideAnimation = createShrinkFadeOutFromBottomAnimation(
+                    mContentContainer,
+                    new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            mPopupWindow.dismiss();
+                        }
+                    });
             // Make the touchable area of this popup be the area specified by mTouchableRegion.
             mPopupWindow.getContentView()
                     .getRootView()
@@ -404,10 +427,12 @@
                 return;
             }
 
-            stopDismissAnimation();
+            mHidden = false;
+            mDismissed = false;
+            stopDismissAndHideAnimations();
             preparePopupContent();
             mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, x, y);
-            growFadeInFromBottom();
+            runShowAnimation();
         }
 
         /**
@@ -418,8 +443,23 @@
                 return;
             }
 
-            mDismissAnimating = true;
-            shrinkFadeOutFromBottom();
+            mHidden = false;
+            mDismissed = true;
+            runDismissAnimation();
+            setZeroTouchableSurface();
+        }
+
+        /**
+         * Hides this popup. This is a no-op if this popup is not showing.
+         * Use {@link #isHidden()} to distinguish between a hidden and a dismissed popup.
+         */
+        public void hide() {
+            if (!isShowing()) {
+                return;
+            }
+
+            mHidden = true;
+            runHideAnimation();
             setZeroTouchableSurface();
         }
 
@@ -427,16 +467,23 @@
          * Returns {@code true} if this popup is currently showing. {@code false} otherwise.
          */
         public boolean isShowing() {
-            return mPopupWindow.isShowing() && !mDismissAnimating;
+            return mPopupWindow.isShowing() && !mDismissed && !mHidden;
+        }
+
+        /**
+         * Returns {@code true} if this popup is currently hidden. {@code false} otherwise.
+         */
+        public boolean isHidden() {
+            return mHidden;
         }
 
         /**
          * Updates the coordinates of this popup.
          * The specified coordinates may be adjusted to make sure the popup is entirely on-screen.
+         * This is a no-op if this popup is not showing.
          */
         public void updateCoordinates(int x, int y) {
-            if (mDismissAnimating) {
-                // Already being dismissed. Ignore.
+            if (!isShowing()) {
                 return;
             }
 
@@ -483,22 +530,29 @@
         }
 
         /**
-         * Performs the "grow and fade in from the bottom" animation on the floating popup.
+         * Performs the "show" animation on the floating popup.
          */
-        private void growFadeInFromBottom() {
-            mGrowFadeInFromBottomAnimation.start();
+        private void runShowAnimation() {
+            mShowAnimation.start();
         }
 
         /**
-         * Performs the "shrink and fade out from bottom" animation on the floating popup.
+         * Performs the "dismiss" animation on the floating popup.
          */
-        private void shrinkFadeOutFromBottom() {
-            mShrinkFadeOutFromBottomAnimation.start();
+        private void runDismissAnimation() {
+            mDismissAnimation.start();
         }
 
-        private void stopDismissAnimation() {
-            mDismissAnimating = false;
-            mShrinkFadeOutFromBottomAnimation.cancel();
+        /**
+         * Performs the "hide" animation on the floating popup.
+         */
+        private void runHideAnimation() {
+            mHideAnimation.start();
+        }
+
+        private void stopDismissAndHideAnimations() {
+            mDismissAnimation.cancel();
+            mHideAnimation.cancel();
         }
 
         /**