Merge "Allow progress bar dimensions to be configured."
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index c626ae9..86ddf9a 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6724,8 +6724,8 @@
                 // Need to clone customContent before adding, because otherwise it can no longer be
                 // parceled independently of remoteViews.
                 customContent = customContent.clone();
-                remoteViews.removeAllViews(R.id.notification_main_column);
-                remoteViews.addView(R.id.notification_main_column, customContent);
+                remoteViews.removeAllViewsExceptId(R.id.notification_main_column, R.id.progress);
+                remoteViews.addView(R.id.notification_main_column, customContent, 0 /* index */);
             }
             // also update the end margin if there is an image
             int endMargin = R.dimen.notification_content_margin_end;
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index e534233..985584e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -99,6 +99,26 @@
      */
     private static final int MAX_NESTED_VIEWS = 10;
 
+    // The unique identifiers for each custom {@link Action}.
+    private static final int SET_ON_CLICK_PENDING_INTENT_TAG = 1;
+    private static final int REFLECTION_ACTION_TAG = 2;
+    private static final int SET_DRAWABLE_PARAMETERS_TAG = 3;
+    private static final int VIEW_GROUP_ACTION_ADD_TAG = 4;
+    private static final int SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG = 5;
+    private static final int SET_EMPTY_VIEW_ACTION_TAG = 6;
+    private static final int VIEW_GROUP_ACTION_REMOVE_TAG = 7;
+    private static final int SET_PENDING_INTENT_TEMPLATE_TAG = 8;
+    private static final int SET_ON_CLICK_FILL_IN_INTENT_TAG = 9;
+    private static final int SET_REMOTE_VIEW_ADAPTER_INTENT_TAG = 10;
+    private static final int TEXT_VIEW_DRAWABLE_ACTION_TAG = 11;
+    private static final int BITMAP_REFLECTION_ACTION_TAG = 12;
+    private static final int TEXT_VIEW_SIZE_ACTION_TAG = 13;
+    private static final int VIEW_PADDING_ACTION_TAG = 14;
+    private static final int SET_REMOTE_VIEW_ADAPTER_LIST_TAG = 15;
+    private static final int TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG = 17;
+    private static final int SET_REMOTE_INPUTS_ACTION_TAG = 18;
+    private static final int LAYOUT_PARAM_ACTION_TAG = 19;
+
     /**
      * Application that hosts the remote views.
      *
@@ -441,8 +461,6 @@
         int viewId;
         int emptyViewId;
 
-        public final static int TAG = 6;
-
         SetEmptyView(int viewId, int emptyViewId) {
             this.viewId = viewId;
             this.emptyViewId = emptyViewId;
@@ -454,7 +472,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(SET_EMPTY_VIEW_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeInt(this.emptyViewId);
         }
@@ -489,7 +507,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_ON_CLICK_FILL_IN_INTENT_TAG);
             dest.writeInt(viewId);
             fillInIntent.writeToParcel(dest, 0 /* no flags */);
         }
@@ -555,8 +573,6 @@
         }
 
         Intent fillInIntent;
-
-        public final static int TAG = 9;
     }
 
     private class SetPendingIntentTemplate extends Action {
@@ -571,7 +587,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_PENDING_INTENT_TEMPLATE_TAG);
             dest.writeInt(viewId);
             pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
         }
@@ -632,8 +648,6 @@
         }
 
         PendingIntent pendingIntentTemplate;
-
-        public final static int TAG = 8;
     }
 
     private class SetRemoteViewsAdapterList extends Action {
@@ -656,7 +670,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_LIST_TAG);
             dest.writeInt(viewId);
             dest.writeInt(viewTypeCount);
 
@@ -715,7 +729,6 @@
 
         int viewTypeCount;
         ArrayList<RemoteViews> list;
-        public final static int TAG = 15;
     }
 
     private class SetRemoteViewsAdapterIntent extends Action {
@@ -730,7 +743,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_INTENT_TAG);
             dest.writeInt(viewId);
             intent.writeToParcel(dest, flags);
         }
@@ -782,8 +795,6 @@
 
         Intent intent;
         boolean isAsync = false;
-
-        public final static int TAG = 10;
     }
 
     /**
@@ -807,7 +818,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_ON_CLICK_PENDING_INTENT_TAG);
             dest.writeInt(viewId);
 
             // We use a flag to indicate whether the parcel contains a valid object.
@@ -860,8 +871,6 @@
         }
 
         PendingIntent pendingIntent;
-
-        public final static int TAG = 1;
     }
 
     private static Rect getSourceBounds(View v) {
@@ -997,7 +1006,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_DRAWABLE_PARAMETERS_TAG);
             dest.writeInt(viewId);
             dest.writeInt(targetBackground ? 1 : 0);
             dest.writeInt(alpha);
@@ -1048,15 +1057,11 @@
         int colorFilter;
         PorterDuff.Mode filterMode;
         int level;
-
-        public final static int TAG = 3;
     }
 
     private final class ReflectionActionWithoutParams extends Action {
         final String methodName;
 
-        public final static int TAG = 5;
-
         ReflectionActionWithoutParams(int viewId, String methodName) {
             this.viewId = viewId;
             this.methodName = methodName;
@@ -1068,7 +1073,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG);
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
         }
@@ -1192,7 +1197,7 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(BITMAP_REFLECTION_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeString(methodName);
             dest.writeInt(bitmapId);
@@ -1214,16 +1219,12 @@
         public String getActionName() {
             return "BitmapReflectionAction";
         }
-
-        public final static int TAG = 12;
     }
 
     /**
      * Base class for the reflection actions.
      */
     private final class ReflectionAction extends Action {
-        static final int TAG = 2;
-
         static final int BOOLEAN = 1;
         static final int BYTE = 2;
         static final int SHORT = 3;
@@ -1330,7 +1331,7 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(TAG);
+            out.writeInt(REFLECTION_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
             out.writeInt(this.type);
@@ -1555,137 +1556,220 @@
     }
 
     /**
-     * Equivalent to calling {@link ViewGroup#addView(View)} after inflating the
-     * given {@link RemoteViews}, or calling {@link ViewGroup#removeAllViews()}
-     * when null. This allows users to build "nested" {@link RemoteViews}.
+     * ViewGroup methods that are related to adding Views.
      */
-    private class ViewGroupAction extends Action {
-        public ViewGroupAction(int viewId, RemoteViews nestedViews) {
+    private class ViewGroupActionAdd extends Action {
+        private RemoteViews mNestedViews;
+        private int mIndex;
+
+        ViewGroupActionAdd(int viewId, RemoteViews nestedViews) {
+            this(viewId, nestedViews, -1 /* index */);
+        }
+
+        ViewGroupActionAdd(int viewId, RemoteViews nestedViews, int index) {
             this.viewId = viewId;
-            this.nestedViews = nestedViews;
+            mNestedViews = nestedViews;
+            mIndex = index;
             if (nestedViews != null) {
                 configureRemoteViewsAsChild(nestedViews);
             }
         }
 
-        ViewGroupAction(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info, int depth) {
+        ViewGroupActionAdd(Parcel parcel, BitmapCache bitmapCache, ApplicationInfo info,
+                int depth) {
             viewId = parcel.readInt();
-            boolean nestedViewsNull = parcel.readInt() == 0;
-            if (!nestedViewsNull) {
-                nestedViews = new RemoteViews(parcel, bitmapCache, info, depth);
-            } else {
-                nestedViews = null;
-            }
+            mNestedViews = new RemoteViews(parcel, bitmapCache, info, depth);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(VIEW_GROUP_ACTION_ADD_TAG);
             dest.writeInt(viewId);
-            if (nestedViews != null) {
-                dest.writeInt(1);
-                nestedViews.writeToParcel(dest, flags);
-            } else {
-                // signifies null
-                dest.writeInt(0);
-            }
+            mNestedViews.writeToParcel(dest, flags);
         }
 
         @Override
         public boolean hasSameAppInfo(ApplicationInfo parentInfo) {
-            return nestedViews != null
-                    && nestedViews.mApplication.packageName.equals(parentInfo.packageName)
-                    && nestedViews.mApplication.uid == parentInfo.uid;
+            return mNestedViews.mApplication.packageName.equals(parentInfo.packageName)
+                    && mNestedViews.mApplication.uid == parentInfo.uid;
         }
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
             final Context context = root.getContext();
             final ViewGroup target = root.findViewById(viewId);
-            if (target == null) return;
-            if (nestedViews != null) {
-                // Inflate nested views and add as children
-                target.addView(nestedViews.apply(context, target, handler));
-            } else {
-                // Clear all children when nested views omitted
-                target.removeAllViews();
+
+            if (target == null) {
+                return;
             }
+
+            // Inflate nested views and add as children
+            target.addView(mNestedViews.apply(context, target, handler), mIndex);
         }
 
         @Override
         public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
             // In the async implementation, update the view tree so that subsequent calls to
-            // findViewById return the currect view.
+            // findViewById return the current view.
             root.createTree();
             ViewTree target = root.findViewTreeById(viewId);
             if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
                 return ACTION_NOOP;
             }
             final ViewGroup targetVg = (ViewGroup) target.mRoot;
-            if (nestedViews == null) {
-                // Clear all children when nested views omitted
-                target.mChildren = null;
-                return new RuntimeAction() {
-                    @Override
-                    public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
-                            throws ActionException {
-                        targetVg.removeAllViews();
-                    }
-                };
-            } else {
-                // Inflate nested views and perform all the async tasks for the child remoteView.
-                final Context context = root.mRoot.getContext();
-                final AsyncApplyTask task = nestedViews.getAsyncApplyTask(
-                        context, targetVg, null, handler);
-                final ViewTree tree = task.doInBackground();
-                if (tree == null) {
-                    throw new ActionException(task.mError);
-                }
 
-                // Update the global view tree, so that next call to findViewTreeById
-                // goes through the subtree as well.
-                target.addChild(tree);
+            // Inflate nested views and perform all the async tasks for the child remoteView.
+            final Context context = root.mRoot.getContext();
+            final AsyncApplyTask task = mNestedViews.getAsyncApplyTask(
+                    context, targetVg, null, handler);
+            final ViewTree tree = task.doInBackground();
 
-                return new RuntimeAction() {
-
-                    @Override
-                    public void apply(View root, ViewGroup rootParent, OnClickHandler handler) throws ActionException {
-                        task.onPostExecute(tree);
-                        targetVg.addView(task.mResult);
-                    }
-                };
+            if (tree == null) {
+                throw new ActionException(task.mError);
             }
+
+            // Update the global view tree, so that next call to findViewTreeById
+            // goes through the subtree as well.
+            target.addChild(tree, mIndex);
+
+            return new RuntimeAction() {
+                @Override
+                public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                        throws ActionException {
+                    task.onPostExecute(tree);
+                    targetVg.addView(task.mResult, mIndex);
+                }
+            };
         }
 
         @Override
         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
-            if (nestedViews != null) {
-                counter.increment(nestedViews.estimateMemoryUsage());
-            }
+            counter.increment(mNestedViews.estimateMemoryUsage());
         }
 
         @Override
         public void setBitmapCache(BitmapCache bitmapCache) {
-            if (nestedViews != null) {
-                nestedViews.setBitmapCache(bitmapCache);
-            }
+            mNestedViews.setBitmapCache(bitmapCache);
         }
 
-        public String getActionName() {
-            return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
-        }
-
+        @Override
         public int mergeBehavior() {
             return MERGE_APPEND;
         }
 
         @Override
         public boolean prefersAsyncApply() {
-            return nestedViews != null && nestedViews.prefersAsyncApply();
+            return mNestedViews.prefersAsyncApply();
         }
 
-        RemoteViews nestedViews;
 
-        public final static int TAG = 4;
+        @Override
+        public String getActionName() {
+            return "ViewGroupActionAdd";
+        }
+    }
+
+    /**
+     * ViewGroup methods related to removing child views.
+     */
+    private class ViewGroupActionRemove extends Action {
+        /**
+         * Id that indicates that all child views of the affected ViewGroup should be removed.
+         *
+         * <p>Using -2 because the default id is -1. This avoids accidentally matching that.
+         */
+        private static final int REMOVE_ALL_VIEWS_ID = -2;
+
+        private int mViewIdToKeep;
+
+        ViewGroupActionRemove(int viewId) {
+            this(viewId, REMOVE_ALL_VIEWS_ID);
+        }
+
+        ViewGroupActionRemove(int viewId, int viewIdToKeep) {
+            this.viewId = viewId;
+            mViewIdToKeep = viewIdToKeep;
+        }
+
+        ViewGroupActionRemove(Parcel parcel) {
+            viewId = parcel.readInt();
+            mViewIdToKeep = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(VIEW_GROUP_ACTION_REMOVE_TAG);
+            dest.writeInt(viewId);
+            dest.writeInt(mViewIdToKeep);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final ViewGroup target = root.findViewById(viewId);
+
+            if (target == null) {
+                return;
+            }
+
+            if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
+                target.removeAllViews();
+                return;
+            }
+
+            removeAllViewsExceptIdToKeep(target);
+        }
+
+        @Override
+        public Action initActionAsync(ViewTree root, ViewGroup rootParent, OnClickHandler handler) {
+            // In the async implementation, update the view tree so that subsequent calls to
+            // findViewById return the current view.
+            root.createTree();
+            ViewTree target = root.findViewTreeById(viewId);
+
+            if ((target == null) || !(target.mRoot instanceof ViewGroup)) {
+                return ACTION_NOOP;
+            }
+
+            final ViewGroup targetVg = (ViewGroup) target.mRoot;
+
+            // Clear all children when nested views omitted
+            target.mChildren = null;
+            return new RuntimeAction() {
+                @Override
+                public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                        throws ActionException {
+                    if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
+                        targetVg.removeAllViews();
+                        return;
+                    }
+
+                    removeAllViewsExceptIdToKeep(targetVg);
+                }
+            };
+        }
+
+        /**
+         * Iterates through the children in the given ViewGroup and removes all the views that
+         * do not have an id of {@link #mViewIdToKeep}.
+         */
+        private void removeAllViewsExceptIdToKeep(ViewGroup viewGroup) {
+            // Otherwise, remove all the views that do not match the id to keep.
+            int index = viewGroup.getChildCount() - 1;
+            while (index >= 0) {
+                if (viewGroup.getChildAt(index).getId() != mViewIdToKeep) {
+                    viewGroup.removeViewAt(index);
+                }
+                index--;
+            }
+        }
+
+        @Override
+        public String getActionName() {
+            return "ViewGroupActionRemove";
+        }
+
+        @Override
+        public int mergeBehavior() {
+            return MERGE_APPEND;
+        }
     }
 
     /**
@@ -1740,7 +1824,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_DRAWABLE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(isRelative ? 1 : 0);
             dest.writeInt(useIcons ? 1 : 0);
@@ -1850,8 +1934,6 @@
 
         boolean drawablesLoaded = false;
         Drawable id1, id2, id3, id4;
-
-        public final static int TAG = 11;
     }
 
     /**
@@ -1871,7 +1953,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_SIZE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(units);
             dest.writeFloat(size);
@@ -1890,8 +1972,6 @@
 
         int units;
         float size;
-
-        public final static int TAG = 13;
     }
 
     /**
@@ -1915,7 +1995,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(VIEW_PADDING_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(left);
             dest.writeInt(top);
@@ -1935,8 +2015,6 @@
         }
 
         int left, top, right, bottom;
-
-        public final static int TAG = 14;
     }
 
     /**
@@ -1968,7 +2046,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(LAYOUT_PARAM_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(property);
             dest.writeInt(value);
@@ -2021,8 +2099,6 @@
 
         int property;
         int value;
-
-        public final static int TAG = 19;
     }
 
     /**
@@ -2057,7 +2133,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(isRelative ? 1 : 0);
             dest.writeInt(index);
@@ -2090,8 +2166,6 @@
         final int index;
         final int color;
         final PorterDuff.Mode mode;
-
-        public final static int TAG = 17;
     }
 
     /**
@@ -2110,7 +2184,7 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TAG);
+            dest.writeInt(SET_REMOTE_INPUTS_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeTypedArray(remoteInputs, flags);
         }
@@ -2128,7 +2202,6 @@
         }
 
         final Parcelable[] remoteInputs;
-        public final static int TAG = 18;
     }
 
     /**
@@ -2271,56 +2344,59 @@
                 for (int i=0; i<count; i++) {
                     int tag = parcel.readInt();
                     switch (tag) {
-                        case SetOnClickPendingIntent.TAG:
+                        case SET_ON_CLICK_PENDING_INTENT_TAG:
                             mActions.add(new SetOnClickPendingIntent(parcel));
                             break;
-                        case SetDrawableParameters.TAG:
+                        case SET_DRAWABLE_PARAMETERS_TAG:
                             mActions.add(new SetDrawableParameters(parcel));
                             break;
-                        case ReflectionAction.TAG:
+                        case REFLECTION_ACTION_TAG:
                             mActions.add(new ReflectionAction(parcel));
                             break;
-                        case ViewGroupAction.TAG:
-                            mActions.add(new ViewGroupAction(parcel, mBitmapCache, mApplication,
+                        case VIEW_GROUP_ACTION_ADD_TAG:
+                            mActions.add(new ViewGroupActionAdd(parcel, mBitmapCache, mApplication,
                                     depth));
                             break;
-                        case ReflectionActionWithoutParams.TAG:
+                        case VIEW_GROUP_ACTION_REMOVE_TAG:
+                            mActions.add(new ViewGroupActionRemove(parcel));
+                            break;
+                        case SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG:
                             mActions.add(new ReflectionActionWithoutParams(parcel));
                             break;
-                        case SetEmptyView.TAG:
+                        case SET_EMPTY_VIEW_ACTION_TAG:
                             mActions.add(new SetEmptyView(parcel));
                             break;
-                        case SetPendingIntentTemplate.TAG:
+                        case SET_PENDING_INTENT_TEMPLATE_TAG:
                             mActions.add(new SetPendingIntentTemplate(parcel));
                             break;
-                        case SetOnClickFillInIntent.TAG:
+                        case SET_ON_CLICK_FILL_IN_INTENT_TAG:
                             mActions.add(new SetOnClickFillInIntent(parcel));
                             break;
-                        case SetRemoteViewsAdapterIntent.TAG:
+                        case SET_REMOTE_VIEW_ADAPTER_INTENT_TAG:
                             mActions.add(new SetRemoteViewsAdapterIntent(parcel));
                             break;
-                        case TextViewDrawableAction.TAG:
+                        case TEXT_VIEW_DRAWABLE_ACTION_TAG:
                             mActions.add(new TextViewDrawableAction(parcel));
                             break;
-                        case TextViewSizeAction.TAG:
+                        case TEXT_VIEW_SIZE_ACTION_TAG:
                             mActions.add(new TextViewSizeAction(parcel));
                             break;
-                        case ViewPaddingAction.TAG:
+                        case VIEW_PADDING_ACTION_TAG:
                             mActions.add(new ViewPaddingAction(parcel));
                             break;
-                        case BitmapReflectionAction.TAG:
+                        case BITMAP_REFLECTION_ACTION_TAG:
                             mActions.add(new BitmapReflectionAction(parcel));
                             break;
-                        case SetRemoteViewsAdapterList.TAG:
+                        case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
                             mActions.add(new SetRemoteViewsAdapterList(parcel));
                             break;
-                        case TextViewDrawableColorFilterAction.TAG:
+                        case TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG:
                             mActions.add(new TextViewDrawableColorFilterAction(parcel));
                             break;
-                        case SetRemoteInputsAction.TAG:
+                        case SET_REMOTE_INPUTS_ACTION_TAG:
                             mActions.add(new SetRemoteInputsAction(parcel));
                             break;
-                        case LayoutParamAction.TAG:
+                        case LAYOUT_PARAM_ACTION_TAG:
                             mActions.add(new LayoutParamAction(parcel));
                             break;
                         default:
@@ -2469,7 +2545,23 @@
      * @param nestedView {@link RemoteViews} that describes the child.
      */
     public void addView(int viewId, RemoteViews nestedView) {
-        addAction(new ViewGroupAction(viewId, nestedView));
+        addAction(nestedView == null
+                ? new ViewGroupActionRemove(viewId)
+                : new ViewGroupActionAdd(viewId, nestedView));
+    }
+
+    /**
+     * Equivalent to calling {@link ViewGroup#addView(View, int)} after inflating the
+     * given {@link RemoteViews}.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to add the child into.
+     * @param nestedView {@link RemoveViews} of the child to add.
+     * @param index The position at which to add the child.
+     *
+     * @hide
+     */
+    public void addView(int viewId, RemoteViews nestedView, int index) {
+        addAction(new ViewGroupActionAdd(viewId, nestedView, index));
     }
 
     /**
@@ -2479,7 +2571,20 @@
      *            children from.
      */
     public void removeAllViews(int viewId) {
-        addAction(new ViewGroupAction(viewId, null));
+        addAction(new ViewGroupActionRemove(viewId));
+    }
+
+    /**
+     * Removes all views in the {@link ViewGroup} specified by the {@code viewId} except for any
+     * child that has the {@code viewIdToKeep} as its id.
+     *
+     * @param viewId The id of the parent {@link ViewGroup} to remove children from.
+     * @param viewIdToKeep The id of a child that should not be removed.
+     *
+     * @hide
+     */
+    public void removeAllViewsExceptId(int viewId, int viewIdToKeep) {
+        addAction(new ViewGroupActionRemove(viewId, viewIdToKeep));
     }
 
     /**
@@ -3652,8 +3757,8 @@
      * and can be searched.
      */
     private static class ViewTree {
+        private static final int INSERT_AT_END_INDEX = -1;
         private View mRoot;
-
         private ArrayList<ViewTree> mChildren;
 
         private ViewTree(View root) {
@@ -3706,11 +3811,26 @@
         }
 
         public void addChild(ViewTree child) {
+            addChild(child, INSERT_AT_END_INDEX);
+        }
+
+        /**
+         * Adds the given {@link ViewTree} as a child at the given index.
+         *
+         * @param index The position at which to add the child or -1 to add last.
+         */
+        public void addChild(ViewTree child, int index) {
             if (mChildren == null) {
                 mChildren = new ArrayList<>();
             }
             child.createTree();
-            mChildren.add(child);
+
+            if (index == INSERT_AT_END_INDEX) {
+                mChildren.add(child);
+                return;
+            }
+
+            mChildren.add(index, child);
         }
 
         private void addViewChild(View v) {
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index ccd26fb..353a1a5 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -15,12 +15,12 @@
   ~ limitations under the License
   -->
 
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/status_bar_latest_event_content"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:tag="base"
-    >
+    android:tag="base" >
     <include layout="@layout/notification_template_header" />
     <LinearLayout
         android:id="@+id/notification_main_column"
@@ -31,19 +31,14 @@
         android:layout_marginEnd="@dimen/notification_content_margin_end"
         android:layout_marginTop="@dimen/notification_content_margin_top"
         android:layout_marginBottom="@dimen/notification_content_margin_bottom"
-        android:orientation="vertical"
-        >
+        android:orientation="vertical" >
         <include layout="@layout/notification_template_part_line1" />
         <include layout="@layout/notification_template_text" />
+        <include
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/notification_progress_bar_height"
+            android:layout_marginTop="@dimen/notification_progress_margin_top"
+            layout="@layout/notification_template_progress" />
     </LinearLayout>
-    <FrameLayout
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_gravity="bottom"
-        android:layout_marginStart="@dimen/notification_content_margin_start"
-        android:layout_marginBottom="15dp"
-        android:layout_marginEnd="@dimen/notification_content_margin_end" >
-        <include layout="@layout/notification_template_progress" />
-    </FrameLayout>
     <include layout="@layout/notification_template_right_icon" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_material_big_base.xml b/core/res/res/layout/notification_template_material_big_base.xml
index 8b0b476..6b1049a 100644
--- a/core/res/res/layout/notification_template_material_big_base.xml
+++ b/core/res/res/layout/notification_template_material_big_base.xml
@@ -20,51 +20,42 @@
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:orientation="vertical"
-    android:tag="big"
-    >
+    android:tag="big" >
     <LinearLayout
             android:id="@+id/notification_action_list_margin_target"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:layout_marginBottom="@dimen/notification_action_list_height"
-            android:orientation="vertical"
-            >
+            android:orientation="vertical" >
         <FrameLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="top"
-            >
+            android:layout_gravity="top" >
             <include layout="@layout/notification_template_header" />
             <LinearLayout
                 android:id="@+id/notification_main_column"
                 android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:layout_gravity="top"
+                android:layout_height="wrap_content"
                 android:layout_marginStart="@dimen/notification_content_margin_start"
                 android:layout_marginEnd="@dimen/notification_content_margin_end"
                 android:layout_marginTop="@dimen/notification_content_margin_top"
                 android:layout_marginBottom="@dimen/notification_content_margin_bottom"
-                android:orientation="vertical"
-                >
+                android:orientation="vertical" >
                 <include layout="@layout/notification_template_part_line1" />
                 <include layout="@layout/notification_template_text" />
+                <include
+                    android:layout_width="match_parent"
+                    android:layout_height="@dimen/notification_progress_bar_height"
+                    android:layout_marginTop="@dimen/notification_progress_margin_top"
+                    layout="@layout/notification_template_progress" />
             </LinearLayout>
-            <FrameLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_gravity="bottom"
-                android:layout_marginStart="@dimen/notification_content_margin_start"
-                android:layout_marginBottom="15dp"
-                android:layout_marginEnd="@dimen/notification_content_margin_end">
-                <include layout="@layout/notification_template_progress" />
-            </FrameLayout>
             <include layout="@layout/notification_template_right_icon" />
         </FrameLayout>
-        <ViewStub android:layout="@layout/notification_material_reply_text"
-                android:id="@+id/notification_material_reply_container"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-        />
+        <ViewStub
+            android:layout="@layout/notification_material_reply_text"
+            android:id="@+id/notification_material_reply_container"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
     </LinearLayout>
     <include layout="@layout/notification_material_action_list" />
 </FrameLayout>
diff --git a/core/res/res/layout/notification_template_progressbar.xml b/core/res/res/layout/notification_template_progressbar.xml
index 61480b8..98e2c03 100644
--- a/core/res/res/layout/notification_template_progressbar.xml
+++ b/core/res/res/layout/notification_template_progressbar.xml
@@ -18,6 +18,6 @@
 <ProgressBar xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@android:id/progress"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
+    android:layout_height="@dimen/notification_progress_bar_height"
     style="@style/Widget.Material.Light.ProgressBar.Horizontal"
     />
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cdc0af7..5e2334d 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -177,6 +177,12 @@
     <!-- height of the content margin on the bottom -->
     <dimen name="notification_content_margin_bottom">16dp</dimen>
 
+    <!-- The height of the progress bar. -->
+    <dimen name="notification_progress_bar_height">15dp</dimen>
+
+    <!-- The top margin before the notification progress bar. -->
+    <dimen name="notification_progress_margin_top">8dp</dimen>
+
     <!-- height of the notification header (for icon and package name) -->
     <dimen name="notification_header_height">48dp</dimen>