Simplifying writeToParcel for various actions

> Using helper methods when writing and reading typed objects
> Moving the tag write outside the action, as it is read outside
  the action as well
> Removing usused tags

Test: am instrument -w -e class android.widget.RemoteViewsTest com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Bug: 65574108
Change-Id: Iaf51cae6ef9f82b70dc1f19c2f019af40c4424a2
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 60f4ed0..fee7d6c 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3839,8 +3839,8 @@
                 contentView.setImageViewBitmap(R.id.profile_badge, profileBadge);
                 contentView.setViewVisibility(R.id.profile_badge, View.VISIBLE);
                 if (isColorized()) {
-                    contentView.setDrawableParameters(R.id.profile_badge, false, -1,
-                            getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP, -1);
+                    contentView.setDrawableTint(R.id.profile_badge, false,
+                            getPrimaryTextColor(), PorterDuff.Mode.SRC_ATOP);
                 }
             }
         }
@@ -4130,18 +4130,14 @@
 
                 if (action != null) {
                     int contrastColor = resolveContrastColor();
-                    contentView.setDrawableParameters(R.id.reply_icon_action,
+                    contentView.setDrawableTint(R.id.reply_icon_action,
                             true /* targetBackground */,
-                            -1,
-                            contrastColor,
-                            PorterDuff.Mode.SRC_ATOP, -1);
+                            contrastColor, PorterDuff.Mode.SRC_ATOP);
                     int iconColor = NotificationColorUtil.isColorLight(contrastColor)
                             ? Color.BLACK : Color.WHITE;
-                    contentView.setDrawableParameters(R.id.reply_icon_action,
+                    contentView.setDrawableTint(R.id.reply_icon_action,
                             false /* targetBackground */,
-                            -1,
-                            iconColor,
-                            PorterDuff.Mode.SRC_ATOP, -1);
+                            iconColor, PorterDuff.Mode.SRC_ATOP);
                     contentView.setOnClickPendingIntent(R.id.right_icon,
                             action.actionIntent);
                     contentView.setOnClickPendingIntent(R.id.reply_icon_action,
@@ -4185,8 +4181,8 @@
 
         private void bindExpandButton(RemoteViews contentView) {
             int color = getPrimaryHighlightColor();
-            contentView.setDrawableParameters(R.id.expand_button, false, -1, color,
-                    PorterDuff.Mode.SRC_ATOP, -1);
+            contentView.setDrawableTint(R.id.expand_button, false, color,
+                    PorterDuff.Mode.SRC_ATOP);
             contentView.setInt(R.id.notification_header, "setOriginalNotificationColor",
                     color);
         }
@@ -4293,8 +4289,7 @@
                 mN.mSmallIcon = Icon.createWithResource(mContext, mN.icon);
             }
             contentView.setImageViewIcon(R.id.icon, mN.mSmallIcon);
-            contentView.setDrawableParameters(R.id.icon, false /* targetBackground */,
-                    -1 /* alpha */, -1 /* colorFilter */, null /* mode */, mN.iconLevel);
+            contentView.setInt(R.id.icon, "setImageLevel", mN.iconLevel);
             processSmallIconColor(mN.mSmallIcon, contentView, ambient);
         }
 
@@ -4684,8 +4679,8 @@
                     bgColor = mContext.getColor(oddAction ? R.color.notification_action_list
                             : R.color.notification_action_list_dark);
                 }
-                button.setDrawableParameters(R.id.button_holder, true, -1, bgColor,
-                        PorterDuff.Mode.SRC_ATOP, -1);
+                button.setDrawableTint(R.id.button_holder, true,
+                        bgColor, PorterDuff.Mode.SRC_ATOP);
                 CharSequence title = action.title;
                 ColorStateList[] outResultColor = null;
                 if (isLegacy()) {
@@ -4818,8 +4813,8 @@
             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
             int color = ambient ? resolveAmbientColor() : getPrimaryHighlightColor();
             if (colorable) {
-                contentView.setDrawableParameters(R.id.icon, false, -1, color,
-                        PorterDuff.Mode.SRC_ATOP, -1);
+                contentView.setDrawableTint(R.id.icon, false, color,
+                        PorterDuff.Mode.SRC_ATOP);
 
             }
             contentView.setInt(R.id.notification_header, "setOriginalIconColor",
@@ -4835,8 +4830,8 @@
             if (largeIcon != null && isLegacy()
                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
                 // resolve color will fall back to the default when legacy
-                contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
-                        PorterDuff.Mode.SRC_ATOP, -1);
+                contentView.setDrawableTint(R.id.icon, false, resolveContrastColor(),
+                        PorterDuff.Mode.SRC_ATOP);
             }
         }
 
@@ -6750,8 +6745,8 @@
                     : NotificationColorUtil.resolveColor(mBuilder.mContext,
                             Notification.COLOR_DEFAULT);
 
-            button.setDrawableParameters(R.id.action0, false, -1, tintColor,
-                    PorterDuff.Mode.SRC_ATOP, -1);
+            button.setDrawableTint(R.id.action0, false, tintColor,
+                    PorterDuff.Mode.SRC_ATOP);
             if (!tombstone) {
                 button.setOnClickPendingIntent(R.id.action0, action.actionIntent);
             }
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 1653afd..2f7dfdd 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -20,6 +20,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
+import android.annotation.NonNull;
 import android.app.ActivityOptions;
 import android.app.ActivityThread;
 import android.app.Application;
@@ -108,9 +109,9 @@
     // 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 SET_DRAWABLE_TINT_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 VIEW_CONTENT_NAVIGATION_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;
@@ -121,7 +122,6 @@
     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;
     private static final int OVERRIDE_TEXT_COLORS_TAG = 20;
@@ -389,10 +389,10 @@
             return MERGE_REPLACE;
         }
 
-        public abstract String getActionName();
+        public abstract int getActionTag();
 
         public String getUniqueKey() {
-            return (getActionName() + viewId);
+            return (getActionTag() + "_" + viewId);
         }
 
         /**
@@ -424,8 +424,8 @@
      */
     private static abstract class RuntimeAction extends Action {
         @Override
-        public final String getActionName() {
-            return "RuntimeAction";
+        public final int getActionTag() {
+            return 0;
         }
 
         @Override
@@ -514,7 +514,6 @@
     }
 
     private class SetEmptyView extends Action {
-        int viewId;
         int emptyViewId;
 
         SetEmptyView(int viewId, int emptyViewId) {
@@ -528,7 +527,6 @@
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(SET_EMPTY_VIEW_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeInt(this.emptyViewId);
         }
@@ -546,8 +544,9 @@
             adapterView.setEmptyView(emptyView);
         }
 
-        public String getActionName() {
-            return "SetEmptyView";
+        @Override
+        public int getActionTag() {
+            return SET_EMPTY_VIEW_ACTION_TAG;
         }
     }
 
@@ -559,13 +558,12 @@
 
         public SetOnClickFillInIntent(Parcel parcel) {
             viewId = parcel.readInt();
-            fillInIntent = Intent.CREATOR.createFromParcel(parcel);
+            fillInIntent = parcel.readTypedObject(Intent.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_ON_CLICK_FILL_IN_INTENT_TAG);
             dest.writeInt(viewId);
-            fillInIntent.writeToParcel(dest, 0 /* no flags */);
+            dest.writeTypedObject(fillInIntent, 0 /* no flags */);
         }
 
         @Override
@@ -624,8 +622,9 @@
             }
         }
 
-        public String getActionName() {
-            return "SetOnClickFillInIntent";
+        @Override
+        public int getActionTag() {
+            return SET_ON_CLICK_FILL_IN_INTENT_TAG;
         }
 
         Intent fillInIntent;
@@ -643,9 +642,8 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_PENDING_INTENT_TEMPLATE_TAG);
             dest.writeInt(viewId);
-            pendingIntentTemplate.writeToParcel(dest, 0 /* no flags */);
+            PendingIntent.writePendingIntentOrNullToParcel(pendingIntentTemplate, dest);
         }
 
         @Override
@@ -699,8 +697,9 @@
             }
         }
 
-        public String getActionName() {
-            return "SetPendingIntentTemplate";
+        @Override
+        public int getActionTag() {
+            return SET_PENDING_INTENT_TEMPLATE_TAG;
         }
 
         PendingIntent pendingIntentTemplate;
@@ -716,30 +715,13 @@
         public SetRemoteViewsAdapterList(Parcel parcel) {
             viewId = parcel.readInt();
             viewTypeCount = parcel.readInt();
-            int count = parcel.readInt();
-            list = new ArrayList<RemoteViews>();
-
-            for (int i = 0; i < count; i++) {
-                RemoteViews rv = RemoteViews.CREATOR.createFromParcel(parcel);
-                list.add(rv);
-            }
+            list = parcel.createTypedArrayList(RemoteViews.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_LIST_TAG);
             dest.writeInt(viewId);
             dest.writeInt(viewTypeCount);
-
-            if (list == null || list.size() == 0) {
-                dest.writeInt(0);
-            } else {
-                int count = list.size();
-                dest.writeInt(count);
-                for (int i = 0; i < count; i++) {
-                    RemoteViews rv = list.get(i);
-                    rv.writeToParcel(dest, flags);
-                }
-            }
+            dest.writeTypedList(list, flags);
         }
 
         @Override
@@ -779,8 +761,9 @@
             }
         }
 
-        public String getActionName() {
-            return "SetRemoteViewsAdapterList";
+        @Override
+        public int getActionTag() {
+            return SET_REMOTE_VIEW_ADAPTER_LIST_TAG;
         }
 
         int viewTypeCount;
@@ -795,13 +778,12 @@
 
         public SetRemoteViewsAdapterIntent(Parcel parcel) {
             viewId = parcel.readInt();
-            intent = Intent.CREATOR.createFromParcel(parcel);
+            intent = parcel.readTypedObject(Intent.CREATOR);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_REMOTE_VIEW_ADAPTER_INTENT_TAG);
             dest.writeInt(viewId);
-            intent.writeToParcel(dest, flags);
+            dest.writeTypedObject(intent, flags);
         }
 
         @Override
@@ -845,8 +827,9 @@
             return copy;
         }
 
-        public String getActionName() {
-            return "SetRemoteViewsAdapterIntent";
+        @Override
+        public int getActionTag() {
+            return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG;
         }
 
         Intent intent;
@@ -866,22 +849,12 @@
 
         public SetOnClickPendingIntent(Parcel parcel) {
             viewId = parcel.readInt();
-
-            // We check a flag to determine if the parcel contains a PendingIntent.
-            if (parcel.readInt() != 0) {
-                pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
-            }
+            pendingIntent = PendingIntent.readPendingIntentOrNullFromParcel(parcel);
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_ON_CLICK_PENDING_INTENT_TAG);
             dest.writeInt(viewId);
-
-            // We use a flag to indicate whether the parcel contains a valid object.
-            dest.writeInt(pendingIntent != null ? 1 : 0);
-            if (pendingIntent != null) {
-                pendingIntent.writeToParcel(dest, 0 /* no flags */);
-            }
+            PendingIntent.writePendingIntentOrNullToParcel(pendingIntent, dest);
         }
 
         @Override
@@ -922,8 +895,9 @@
             target.setOnClickListener(listener);
         }
 
-        public String getActionName() {
-            return "SetOnClickPendingIntent";
+        @Override
+        public int getActionTag() {
+            return SET_ON_CLICK_PENDING_INTENT_TAG;
         }
 
         PendingIntent pendingIntent;
@@ -1012,55 +986,37 @@
     }
 
     /**
-     * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
+     * Equivalent to calling
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
-     * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given view.
+     * on the {@link Drawable} of a given view.
      * <p>
-     * These operations will be performed on the {@link Drawable} returned by the
+     * The operation will be performed on the {@link Drawable} returned by the
      * target {@link View#getBackground()} by default.  If targetBackground is false,
      * we assume the target is an {@link ImageView} and try applying the operations
      * to {@link ImageView#getDrawable()}.
      * <p>
-     * You can omit specific calls by marking their values with null or -1.
      */
-    private class SetDrawableParameters extends Action {
-        public SetDrawableParameters(int id, boolean targetBackground, int alpha,
-                int colorFilter, PorterDuff.Mode mode, int level) {
+    private class SetDrawableTint extends Action {
+        SetDrawableTint(int id, boolean targetBackground,
+                int colorFilter, @NonNull PorterDuff.Mode mode) {
             this.viewId = id;
             this.targetBackground = targetBackground;
-            this.alpha = alpha;
             this.colorFilter = colorFilter;
             this.filterMode = mode;
-            this.level = level;
         }
 
-        public SetDrawableParameters(Parcel parcel) {
+        SetDrawableTint(Parcel parcel) {
             viewId = parcel.readInt();
             targetBackground = parcel.readInt() != 0;
-            alpha = parcel.readInt();
             colorFilter = parcel.readInt();
-            boolean hasMode = parcel.readInt() != 0;
-            if (hasMode) {
-                filterMode = PorterDuff.Mode.valueOf(parcel.readString());
-            } else {
-                filterMode = null;
-            }
-            level = parcel.readInt();
+            filterMode = PorterDuff.intToMode(parcel.readInt());
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_DRAWABLE_PARAMETERS_TAG);
             dest.writeInt(viewId);
             dest.writeInt(targetBackground ? 1 : 0);
-            dest.writeInt(alpha);
             dest.writeInt(colorFilter);
-            if (filterMode != null) {
-                dest.writeInt(1);
-                dest.writeString(filterMode.toString());
-            } else {
-                dest.writeInt(0);
-            }
-            dest.writeInt(level);
+            dest.writeInt(PorterDuff.modeToInt(filterMode));
         }
 
         @Override
@@ -1078,47 +1034,36 @@
             }
 
             if (targetDrawable != null) {
-                // Perform modifications only if values are set correctly
-                if (alpha != -1) {
-                    targetDrawable.mutate().setAlpha(alpha);
-                }
-                if (filterMode != null) {
-                    targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
-                }
-                if (level != -1) {
-                    targetDrawable.mutate().setLevel(level);
-                }
+                targetDrawable.mutate().setColorFilter(colorFilter, filterMode);
             }
         }
 
-        public String getActionName() {
-            return "SetDrawableParameters";
+        @Override
+        public int getActionTag() {
+            return SET_DRAWABLE_TINT_TAG;
         }
 
         boolean targetBackground;
-        int alpha;
         int colorFilter;
         PorterDuff.Mode filterMode;
-        int level;
     }
 
-    private final class ReflectionActionWithoutParams extends Action {
-        final String methodName;
+    private final class ViewContentNavigation extends Action {
+        final boolean mNext;
 
-        ReflectionActionWithoutParams(int viewId, String methodName) {
+        ViewContentNavigation(int viewId, boolean next) {
             this.viewId = viewId;
-            this.methodName = methodName;
+            this.mNext = next;
         }
 
-        ReflectionActionWithoutParams(Parcel in) {
+        ViewContentNavigation(Parcel in) {
             this.viewId = in.readInt();
-            this.methodName = in.readString();
+            this.mNext = in.readBoolean();
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG);
             out.writeInt(this.viewId);
-            out.writeString(this.methodName);
+            out.writeBoolean(this.mNext);
         }
 
         @Override
@@ -1127,23 +1072,20 @@
             if (view == null) return;
 
             try {
-                getMethod(view, this.methodName, null, false /* async */).invoke(view);
+                getMethod(view,
+                        mNext ? "showNext" : "showPrevious", null, false /* async */).invoke(view);
             } catch (Throwable ex) {
                 throw new ActionException(ex);
             }
         }
 
         public int mergeBehavior() {
-            // we don't need to build up showNext or showPrevious calls
-            if (methodName.equals("showNext") || methodName.equals("showPrevious")) {
-                return MERGE_IGNORE;
-            } else {
-                return MERGE_REPLACE;
-            }
+            return MERGE_IGNORE;
         }
 
-        public String getActionName() {
-            return "ReflectionActionWithoutParams";
+        @Override
+        public int getActionTag() {
+            return VIEW_CONTENT_NAVIGATION_TAG;
         }
     }
 
@@ -1157,12 +1099,7 @@
         }
 
         public BitmapCache(Parcel source) {
-            int count = source.readInt();
-            mBitmaps = new ArrayList<>(count);
-            for (int i = 0; i < count; i++) {
-                Bitmap b = Bitmap.CREATOR.createFromParcel(source);
-                mBitmaps.add(b);
-            }
+            mBitmaps = source.createTypedArrayList(Bitmap.CREATOR);
         }
 
         public int getBitmapId(Bitmap b) {
@@ -1188,11 +1125,7 @@
         }
 
         public void writeBitmapsToParcel(Parcel dest, int flags) {
-            int count = mBitmaps.size();
-            dest.writeInt(count);
-            for (int i = 0; i < count; i++) {
-                mBitmaps.get(i).writeToParcel(dest, flags);
-            }
+            dest.writeTypedList(mBitmaps, flags);
         }
 
         public int getBitmapMemory() {
@@ -1228,7 +1161,6 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(BITMAP_REFLECTION_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeString(methodName);
             dest.writeInt(bitmapId);
@@ -1247,8 +1179,9 @@
             bitmapId = bitmapCache.getBitmapId(bitmap);
         }
 
-        public String getActionName() {
-            return "BitmapReflectionAction";
+        @Override
+        public int getActionTag() {
+            return BITMAP_REFLECTION_ACTION_TAG;
         }
     }
 
@@ -1300,7 +1233,7 @@
             // written to the parcel.
             switch (this.type) {
                 case BOOLEAN:
-                    this.value = in.readInt() != 0;
+                    this.value = in.readBoolean();
                     break;
                 case BYTE:
                     this.value = in.readByte();
@@ -1330,39 +1263,28 @@
                     this.value = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
                     break;
                 case URI:
-                    if (in.readInt() != 0) {
-                        this.value = Uri.CREATOR.createFromParcel(in);
-                    }
+                    this.value = in.readTypedObject(Uri.CREATOR);
                     break;
                 case BITMAP:
-                    if (in.readInt() != 0) {
-                        this.value = Bitmap.CREATOR.createFromParcel(in);
-                    }
+                    this.value = in.readTypedObject(Bitmap.CREATOR);
                     break;
                 case BUNDLE:
                     this.value = in.readBundle();
                     break;
                 case INTENT:
-                    if (in.readInt() != 0) {
-                        this.value = Intent.CREATOR.createFromParcel(in);
-                    }
+                    this.value = in.readTypedObject(Intent.CREATOR);
                     break;
                 case COLOR_STATE_LIST:
-                    if (in.readInt() != 0) {
-                        this.value = ColorStateList.CREATOR.createFromParcel(in);
-                    }
+                    this.value = in.readTypedObject(ColorStateList.CREATOR);
                     break;
                 case ICON:
-                    if (in.readInt() != 0) {
-                        this.value = Icon.CREATOR.createFromParcel(in);
-                    }
+                    this.value = in.readTypedObject(Icon.CREATOR);
                 default:
                     break;
             }
         }
 
         public void writeToParcel(Parcel out, int flags) {
-            out.writeInt(REFLECTION_ACTION_TAG);
             out.writeInt(this.viewId);
             out.writeString(this.methodName);
             out.writeInt(this.type);
@@ -1376,7 +1298,7 @@
             // we have written a valid value to the parcel.
             switch (this.type) {
                 case BOOLEAN:
-                    out.writeInt((Boolean) this.value ? 1 : 0);
+                    out.writeBoolean((Boolean) this.value);
                     break;
                 case BYTE:
                     out.writeByte((Byte) this.value);
@@ -1413,10 +1335,7 @@
                 case INTENT:
                 case COLOR_STATE_LIST:
                 case ICON:
-                    out.writeInt(this.value != null ? 1 : 0);
-                    if (this.value != null) {
-                        ((Parcelable) this.value).writeToParcel(out, flags);
-                    }
+                    out.writeTypedObject((Parcelable) this.value, flags);
                     break;
                 default:
                     break;
@@ -1522,10 +1441,16 @@
             }
         }
 
-        public String getActionName() {
+        @Override
+        public int getActionTag() {
+            return REFLECTION_ACTION_TAG;
+        }
+
+        @Override
+        public String getUniqueKey() {
             // Each type of reflection action corresponds to a setter, so each should be seen as
             // unique from the standpoint of merging.
-            return "ReflectionAction" + this.methodName + this.type;
+            return super.getUniqueKey() + this.methodName + this.type;
         }
 
         @Override
@@ -1587,7 +1512,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(VIEW_GROUP_ACTION_ADD_TAG);
             dest.writeInt(viewId);
             dest.writeInt(mIndex);
             mNestedViews.writeToParcel(dest, flags);
@@ -1662,10 +1586,9 @@
             return mNestedViews.prefersAsyncApply();
         }
 
-
         @Override
-        public String getActionName() {
-            return "ViewGroupActionAdd";
+        public int getActionTag() {
+            return VIEW_GROUP_ACTION_ADD_TAG;
         }
     }
 
@@ -1697,7 +1620,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(VIEW_GROUP_ACTION_REMOVE_TAG);
             dest.writeInt(viewId);
             dest.writeInt(mViewIdToKeep);
         }
@@ -1763,8 +1685,8 @@
         }
 
         @Override
-        public String getActionName() {
-            return "ViewGroupActionRemove";
+        public int getActionTag() {
+            return VIEW_GROUP_ACTION_REMOVE_TAG;
         }
 
         @Override
@@ -1804,18 +1726,10 @@
             isRelative = (parcel.readInt() != 0);
             useIcons = (parcel.readInt() != 0);
             if (useIcons) {
-                if (parcel.readInt() != 0) {
-                    i1 = Icon.CREATOR.createFromParcel(parcel);
-                }
-                if (parcel.readInt() != 0) {
-                    i2 = Icon.CREATOR.createFromParcel(parcel);
-                }
-                if (parcel.readInt() != 0) {
-                    i3 = Icon.CREATOR.createFromParcel(parcel);
-                }
-                if (parcel.readInt() != 0) {
-                    i4 = Icon.CREATOR.createFromParcel(parcel);
-                }
+                i1 = parcel.readTypedObject(Icon.CREATOR);
+                i2 = parcel.readTypedObject(Icon.CREATOR);
+                i3 = parcel.readTypedObject(Icon.CREATOR);
+                i4 = parcel.readTypedObject(Icon.CREATOR);
             } else {
                 d1 = parcel.readInt();
                 d2 = parcel.readInt();
@@ -1825,35 +1739,14 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TEXT_VIEW_DRAWABLE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(isRelative ? 1 : 0);
             dest.writeInt(useIcons ? 1 : 0);
             if (useIcons) {
-                if (i1 != null) {
-                    dest.writeInt(1);
-                    i1.writeToParcel(dest, 0);
-                } else {
-                    dest.writeInt(0);
-                }
-                if (i2 != null) {
-                    dest.writeInt(1);
-                    i2.writeToParcel(dest, 0);
-                } else {
-                    dest.writeInt(0);
-                }
-                if (i3 != null) {
-                    dest.writeInt(1);
-                    i3.writeToParcel(dest, 0);
-                } else {
-                    dest.writeInt(0);
-                }
-                if (i4 != null) {
-                    dest.writeInt(1);
-                    i4.writeToParcel(dest, 0);
-                } else {
-                    dest.writeInt(0);
-                }
+                dest.writeTypedObject(i1, 0);
+                dest.writeTypedObject(i2, 0);
+                dest.writeTypedObject(i3, 0);
+                dest.writeTypedObject(i4, 0);
             } else {
                 dest.writeInt(d1);
                 dest.writeInt(d2);
@@ -1924,8 +1817,9 @@
             return useIcons;
         }
 
-        public String getActionName() {
-            return "TextViewDrawableAction";
+        @Override
+        public int getActionTag() {
+            return TEXT_VIEW_DRAWABLE_ACTION_TAG;
         }
 
         boolean isRelative = false;
@@ -1954,7 +1848,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TEXT_VIEW_SIZE_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(units);
             dest.writeFloat(size);
@@ -1967,8 +1860,9 @@
             target.setTextSize(units, size);
         }
 
-        public String getActionName() {
-            return "TextViewSizeAction";
+        @Override
+        public int getActionTag() {
+            return TEXT_VIEW_SIZE_ACTION_TAG;
         }
 
         int units;
@@ -1996,7 +1890,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(VIEW_PADDING_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeInt(left);
             dest.writeInt(top);
@@ -2011,8 +1904,9 @@
             target.setPadding(left, top, right, bottom);
         }
 
-        public String getActionName() {
-            return "ViewPaddingAction";
+        @Override
+        public int getActionTag() {
+            return VIEW_PADDING_ACTION_TAG;
         }
 
         int left, top, right, bottom;
@@ -2029,6 +1923,9 @@
         public static final int LAYOUT_WIDTH = 2;
         public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
 
+        final int mProperty;
+        final int mValue;
+
         /**
          * @param viewId ID of the view alter
          * @param property which layout parameter to alter
@@ -2036,21 +1933,20 @@
          */
         public LayoutParamAction(int viewId, int property, int value) {
             this.viewId = viewId;
-            this.property = property;
-            this.value = value;
+            this.mProperty = property;
+            this.mValue = value;
         }
 
         public LayoutParamAction(Parcel parcel) {
             viewId = parcel.readInt();
-            property = parcel.readInt();
-            value = parcel.readInt();
+            mProperty = parcel.readInt();
+            mValue = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(LAYOUT_PARAM_ACTION_TAG);
             dest.writeInt(viewId);
-            dest.writeInt(property);
-            dest.writeInt(value);
+            dest.writeInt(mProperty);
+            dest.writeInt(mValue);
         }
 
         @Override
@@ -2063,27 +1959,27 @@
             if (layoutParams == null) {
                 return;
             }
-            switch (property) {
+            switch (mProperty) {
                 case LAYOUT_MARGIN_END_DIMEN:
                     if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
-                        int resolved = resolveDimenPixelOffset(target, value);
+                        int resolved = resolveDimenPixelOffset(target, mValue);
                         ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(resolved);
                         target.setLayoutParams(layoutParams);
                     }
                     break;
                 case LAYOUT_MARGIN_BOTTOM_DIMEN:
                     if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
-                        int resolved = resolveDimenPixelOffset(target, value);
+                        int resolved = resolveDimenPixelOffset(target, mValue);
                         ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
                         target.setLayoutParams(layoutParams);
                     }
                     break;
                 case LAYOUT_WIDTH:
-                    layoutParams.width = value;
+                    layoutParams.width = mValue;
                     target.setLayoutParams(layoutParams);
                     break;
                 default:
-                    throw new IllegalArgumentException("Unknown property " + property);
+                    throw new IllegalArgumentException("Unknown property " + mProperty);
             }
         }
 
@@ -2094,79 +1990,15 @@
             return target.getContext().getResources().getDimensionPixelOffset(value);
         }
 
-        public String getActionName() {
-            return "LayoutParamAction" + property + ".";
-        }
-
-        int property;
-        int value;
-    }
-
-    /**
-     * Helper action to set a color filter on a compound drawable on a TextView. Supports relative
-     * (s/t/e/b) or cardinal (l/t/r/b) arrangement.
-     */
-    private class TextViewDrawableColorFilterAction extends Action {
-        public TextViewDrawableColorFilterAction(int viewId, boolean isRelative, int index,
-                int color, PorterDuff.Mode mode) {
-            this.viewId = viewId;
-            this.isRelative = isRelative;
-            this.index = index;
-            this.color = color;
-            this.mode = mode;
-        }
-
-        public TextViewDrawableColorFilterAction(Parcel parcel) {
-            viewId = parcel.readInt();
-            isRelative = (parcel.readInt() != 0);
-            index = parcel.readInt();
-            color = parcel.readInt();
-            mode = readPorterDuffMode(parcel);
-        }
-
-        private PorterDuff.Mode readPorterDuffMode(Parcel parcel) {
-            int mode = parcel.readInt();
-            if (mode >= 0 && mode < PorterDuff.Mode.values().length) {
-                return PorterDuff.Mode.values()[mode];
-            } else {
-                return PorterDuff.Mode.CLEAR;
-            }
-        }
-
-        public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG);
-            dest.writeInt(viewId);
-            dest.writeInt(isRelative ? 1 : 0);
-            dest.writeInt(index);
-            dest.writeInt(color);
-            dest.writeInt(mode.ordinal());
+        @Override
+        public int getActionTag() {
+            return LAYOUT_PARAM_ACTION_TAG;
         }
 
         @Override
-        public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
-            final TextView target = root.findViewById(viewId);
-            if (target == null) return;
-            Drawable[] drawables = isRelative
-                    ? target.getCompoundDrawablesRelative()
-                    : target.getCompoundDrawables();
-            if (index < 0 || index >= 4) {
-                throw new IllegalStateException("index must be in range [0, 3].");
-            }
-            Drawable d = drawables[index];
-            if (d != null) {
-                d.mutate();
-                d.setColorFilter(color, mode);
-            }
+        public String getUniqueKey() {
+            return super.getUniqueKey() + mProperty;
         }
-
-        public String getActionName() {
-            return "TextViewDrawableColorFilterAction";
-        }
-
-        final boolean isRelative;
-        final int index;
-        final int color;
-        final PorterDuff.Mode mode;
     }
 
     /**
@@ -2185,7 +2017,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(SET_REMOTE_INPUTS_ACTION_TAG);
             dest.writeInt(viewId);
             dest.writeTypedArray(remoteInputs, flags);
         }
@@ -2198,8 +2029,9 @@
             target.setTagInternal(R.id.remote_input_tag, remoteInputs);
         }
 
-        public String getActionName() {
-            return "SetRemoteInputsAction";
+        @Override
+        public int getActionTag() {
+            return SET_REMOTE_INPUTS_ACTION_TAG;
         }
 
         final Parcelable[] remoteInputs;
@@ -2221,7 +2053,6 @@
         }
 
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeInt(OVERRIDE_TEXT_COLORS_TAG);
             dest.writeInt(textColor);
         }
 
@@ -2246,8 +2077,9 @@
             }
         }
 
-        public String getActionName() {
-            return "OverrideTextColorsAction";
+        @Override
+        public int getActionTag() {
+            return OVERRIDE_TEXT_COLORS_TAG;
         }
     }
 
@@ -2411,16 +2243,16 @@
         switch (tag) {
             case SET_ON_CLICK_PENDING_INTENT_TAG:
                 return new SetOnClickPendingIntent(parcel);
-            case SET_DRAWABLE_PARAMETERS_TAG:
-                return new SetDrawableParameters(parcel);
+            case SET_DRAWABLE_TINT_TAG:
+                return new SetDrawableTint(parcel);
             case REFLECTION_ACTION_TAG:
                 return new ReflectionAction(parcel);
             case VIEW_GROUP_ACTION_ADD_TAG:
                 return new ViewGroupActionAdd(parcel, mBitmapCache, mApplication, depth);
             case VIEW_GROUP_ACTION_REMOVE_TAG:
                 return new ViewGroupActionRemove(parcel);
-            case SET_REFLECTION_ACTION_WITHOUT_PARAMS_TAG:
-                return new ReflectionActionWithoutParams(parcel);
+            case VIEW_CONTENT_NAVIGATION_TAG:
+                return new ViewContentNavigation(parcel);
             case SET_EMPTY_VIEW_ACTION_TAG:
                 return new SetEmptyView(parcel);
             case SET_PENDING_INTENT_TEMPLATE_TAG:
@@ -2439,8 +2271,6 @@
                 return new BitmapReflectionAction(parcel);
             case SET_REMOTE_VIEW_ADAPTER_LIST_TAG:
                 return new SetRemoteViewsAdapterList(parcel);
-            case TEXT_VIEW_DRAWABLE_COLOR_FILTER_ACTION_TAG:
-                return new TextViewDrawableColorFilterAction(parcel);
             case SET_REMOTE_INPUTS_ACTION_TAG:
                 return new SetRemoteInputsAction(parcel);
             case LAYOUT_PARAM_ACTION_TAG:
@@ -2597,7 +2427,7 @@
      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showNext()}
      */
     public void showNext(int viewId) {
-        addAction(new ReflectionActionWithoutParams(viewId, "showNext"));
+        addAction(new ViewContentNavigation(viewId, true /* next */));
     }
 
     /**
@@ -2606,7 +2436,7 @@
      * @param viewId The id of the view on which to call {@link AdapterViewAnimator#showPrevious()}
      */
     public void showPrevious(int viewId) {
-        addAction(new ReflectionActionWithoutParams(viewId, "showPrevious"));
+        addAction(new ViewContentNavigation(viewId, false /* next */));
     }
 
     /**
@@ -2680,28 +2510,6 @@
     }
 
     /**
-     * Equivalent to applying a color filter on one of the drawables in
-     * {@link android.widget.TextView#getCompoundDrawablesRelative()}.
-     *
-     * @param viewId The id of the view whose text should change.
-     * @param index  The index of the drawable in the array of
-     *               {@link android.widget.TextView#getCompoundDrawablesRelative()} to set the color
-     *               filter on. Must be in [0, 3].
-     * @param color  The color of the color filter. See
-     *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
-     * @param mode   The mode of the color filter. See
-     *               {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)}.
-     * @hide
-     */
-    public void setTextViewCompoundDrawablesRelativeColorFilter(int viewId,
-            int index, int color, PorterDuff.Mode mode) {
-        if (index < 0 || index >= 4) {
-            throw new IllegalArgumentException("index must be in range [0, 3].");
-        }
-        addAction(new TextViewDrawableColorFilterAction(viewId, true, index, color, mode));
-    }
-
-    /**
      * Equivalent to calling {@link
      * TextView#setCompoundDrawablesWithIntrinsicBounds(Drawable, Drawable, Drawable, Drawable)}
      * using the drawables yielded by {@link Icon#loadDrawable(Context)}.
@@ -2898,12 +2706,10 @@
 
     /**
      * @hide
-     * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
+     * Equivalent to calling
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
-     * and/or {@link Drawable#setLevel(int)} on the {@link Drawable} of a given
-     * view.
+     * on the {@link Drawable} of a given view.
      * <p>
-     * You can omit specific calls by marking their values with null or -1.
      *
      * @param viewId The id of the view that contains the target
      *            {@link Drawable}
@@ -2912,20 +2718,15 @@
      *            {@link android.view.View#getBackground()}. Otherwise, assume
      *            the target view is an {@link ImageView} and apply them to
      *            {@link ImageView#getDrawable()}.
-     * @param alpha Specify an alpha value for the drawable, or -1 to leave
-     *            unchanged.
      * @param colorFilter Specify a color for a
      *            {@link android.graphics.ColorFilter} for this drawable. This will be ignored if
      *            {@code mode} is {@code null}.
      * @param mode Specify a PorterDuff mode for this drawable, or null to leave
      *            unchanged.
-     * @param level Specify the level for the drawable, or -1 to leave
-     *            unchanged.
      */
-    public void setDrawableParameters(int viewId, boolean targetBackground, int alpha,
-            int colorFilter, PorterDuff.Mode mode, int level) {
-        addAction(new SetDrawableParameters(viewId, targetBackground, alpha,
-                colorFilter, mode, level));
+    public void setDrawableTint(int viewId, boolean targetBackground,
+            int colorFilter, @NonNull PorterDuff.Mode mode) {
+        addAction(new SetDrawableTint(viewId, targetBackground, colorFilter, mode));
     }
 
     /**
@@ -3717,6 +3518,7 @@
         parcel.writeInt(count);
         for (int i = 0; i < count; i++) {
             Action a = mActions.get(i);
+            parcel.writeInt(a.getActionTag());
             a.writeToParcel(parcel, a.hasSameAppInfo(mApplication)
                     ? PARCELABLE_ELIDE_DUPLICATES : 0);
         }