Improve RemoteViews to simplify notification view hierarchy.

Test: manual
Change-Id: Iab1579a3e992a53f1e44ff7236511d1093de048f
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 587a7cc..4c5044d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -6701,8 +6701,7 @@
             customContent = customContent.clone();
             if (p.mHeaderless) {
                 if (decorationType <= DevFlags.DECORATION_PARTIAL) {
-                    template.removeAllViewsExceptId(R.id.notification_top_line_container,
-                            R.id.notification_main_column);
+                    template.removeFromParent(R.id.notification_top_line);
                 }
                 if (decorationType != DevFlags.DECORATION_FULL_COMPATIBLE) {
                     // Change the max content size from 60dp (the compatible size) to 48dp
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 4a84851..5176b7e 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -73,6 +73,8 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewGroup.MarginLayoutParams;
+import android.view.ViewManager;
+import android.view.ViewParent;
 import android.view.ViewStub;
 import android.widget.AdapterView.OnItemClickListener;
 
@@ -175,6 +177,7 @@
     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
     private static final int SET_RIPPLE_DRAWABLE_COLOR_TAG = 21;
     private static final int SET_INT_TAG_TAG = 22;
+    private static final int REMOVE_FROM_PARENT_ACTION_TAG = 23;
 
     /** @hide **/
     @IntDef(prefix = "MARGIN_", value = {
@@ -1827,6 +1830,75 @@
     }
 
     /**
+     * Action to remove a view from its parent.
+     */
+    private class RemoveFromParentAction extends Action {
+
+        RemoveFromParentAction(@IdRes int viewId) {
+            this.viewId = viewId;
+        }
+
+        RemoveFromParentAction(Parcel parcel) {
+            viewId = parcel.readInt();
+        }
+
+        public void writeToParcel(Parcel dest, int flags) {
+            dest.writeInt(viewId);
+        }
+
+        @Override
+        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
+            final View target = root.findViewById(viewId);
+
+            if (target == null || target == root) {
+                return;
+            }
+
+            ViewParent parent = target.getParent();
+            if (parent instanceof ViewManager) {
+                ((ViewManager) parent).removeView(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 correct view.
+            root.createTree();
+            ViewTree target = root.findViewTreeById(viewId);
+
+            if (target == null || target == root) {
+                return ACTION_NOOP;
+            }
+
+            ViewTree parent = root.findViewTreeParentOf(target);
+            if (parent == null || !(parent.mRoot instanceof ViewManager)) {
+                return ACTION_NOOP;
+            }
+            final ViewManager parentVg = (ViewManager) parent.mRoot;
+
+            parent.mChildren.remove(target);
+            return new RuntimeAction() {
+                @Override
+                public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
+                        throws ActionException {
+                    parentVg.removeView(target.mRoot);
+                }
+            };
+        }
+
+        @Override
+        public int getActionTag() {
+            return REMOVE_FROM_PARENT_ACTION_TAG;
+        }
+
+        @Override
+        public int mergeBehavior() {
+            return MERGE_APPEND;
+        }
+    }
+
+    /**
      * Helper action to set compound drawables on a TextView. Supports relative
      * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
      */
@@ -2531,6 +2603,8 @@
                 return new SetRippleDrawableColor(parcel);
             case SET_INT_TAG_TAG:
                 return new SetIntTagAction(parcel);
+            case REMOVE_FROM_PARENT_ACTION_TAG:
+                return new RemoveFromParentAction(parcel);
             default:
                 throw new ActionException("Tag " + tag + " not found");
         }
@@ -2669,6 +2743,18 @@
     }
 
     /**
+     * Removes the {@link View} specified by the {@code viewId} from its parent {@link ViewManager}.
+     * This will do nothing if the viewId specifies the root view of this RemoteViews.
+     *
+     * @param viewId The id of the {@link View} to remove from its parent.
+     *
+     * @hide
+     */
+    public void removeFromParent(@IdRes int viewId) {
+        addAction(new RemoveFromParentAction(viewId));
+    }
+
+    /**
      * Equivalent to calling {@link AdapterViewAnimator#showNext()}
      *
      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
@@ -4013,6 +4099,22 @@
             return null;
         }
 
+        public ViewTree findViewTreeParentOf(ViewTree child) {
+            if (mChildren == null) {
+                return null;
+            }
+            for (ViewTree tree : mChildren) {
+                if (tree == child) {
+                    return this;
+                }
+                ViewTree result = tree.findViewTreeParentOf(child);
+                if (result != null) {
+                    return result;
+                }
+            }
+            return null;
+        }
+
         public void replaceView(View v) {
             mRoot = v;
             mChildren = null;
diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml
index a045e15..41be36b 100644
--- a/core/res/res/layout/notification_template_material_base.xml
+++ b/core/res/res/layout/notification_template_material_base.xml
@@ -109,51 +109,38 @@
             android:layout_weight="1"
             />
 
-        <!--
-        Because RemoteViews doesn't allow us to remove specific views, this container allows us
-        to remove the NotificationTopLineView without removing other padding items.
-        -->
-        <FrameLayout
-            android:id="@+id/notification_top_line_container"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
+        <!-- extends ViewGroup -->
+        <NotificationTopLineView
+            android:id="@+id/notification_top_line"
+            android:layout_width="wrap_content"
+            android:layout_height="@dimen/notification_headerless_line_height"
+            android:layout_marginEnd="@dimen/notification_heading_margin_end"
+            android:layout_marginStart="@dimen/notification_content_margin_start"
             android:clipChildren="false"
+            android:theme="@style/Theme.DeviceDefault.Notification"
             >
 
-            <!-- extends ViewGroup -->
-            <NotificationTopLineView
-                android:id="@+id/notification_top_line"
+            <!--
+            NOTE: The notification_top_line_views layout contains the app_name_text.
+            In order to include the title view at the beginning, the Notification.Builder
+            has logic to hide that view whenever this title view is to be visible.
+            -->
+
+            <TextView
+                android:id="@+id/title"
                 android:layout_width="wrap_content"
-                android:layout_height="@dimen/notification_headerless_line_height"
-                android:layout_marginEnd="@dimen/notification_heading_margin_end"
-                android:layout_marginStart="@dimen/notification_content_margin_start"
-                android:clipChildren="false"
-                android:theme="@style/Theme.DeviceDefault.Notification"
-                >
+                android:layout_height="wrap_content"
+                android:layout_marginEnd="@dimen/notification_header_separating_margin"
+                android:ellipsize="marquee"
+                android:fadingEdge="horizontal"
+                android:singleLine="true"
+                android:textAlignment="viewStart"
+                android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
+                />
 
-                <!--
-                NOTE: The notification_top_line_views layout contains the app_name_text.
-                In order to include the title view at the beginning, the Notification.Builder
-                has logic to hide that view whenever this title view is to be visible.
-                -->
+            <include layout="@layout/notification_top_line_views" />
 
-                <TextView
-                    android:id="@+id/title"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginEnd="@dimen/notification_header_separating_margin"
-                    android:ellipsize="marquee"
-                    android:fadingEdge="horizontal"
-                    android:singleLine="true"
-                    android:textAlignment="viewStart"
-                    android:textAppearance="@style/TextAppearance.DeviceDefault.Notification.Title"
-                    />
-
-                <include layout="@layout/notification_top_line_views" />
-
-            </NotificationTopLineView>
-
-        </FrameLayout>
+        </NotificationTopLineView>
 
         <LinearLayout
             android:id="@+id/notification_main_column"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index beb0059..9b06285 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2876,7 +2876,6 @@
   <java-symbol type="id" name="alternate_expand_target" />
   <java-symbol type="id" name="notification_header" />
   <java-symbol type="id" name="notification_top_line" />
-  <java-symbol type="id" name="notification_top_line_container" />
   <java-symbol type="id" name="notification_headerless_margin_extra_top" />
   <java-symbol type="id" name="notification_headerless_margin_extra_bottom" />
   <java-symbol type="id" name="time_divider" />