Merge "Use the correct prebuilts for lifecycles."
diff --git a/buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt b/buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt
new file mode 100644
index 0000000..55f010f
--- /dev/null
+++ b/buildSrc/src/main/kotlin/android/support/ErrorProneConfiguration.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.support
+
+import net.ltgt.gradle.errorprone.ErrorProneToolChain
+import org.gradle.api.tasks.compile.JavaCompile
+
+fun JavaCompile.configureWithErrorProne(toolChain: ErrorProneToolChain) {
+    this.toolChain = toolChain
+
+    val compilerArgs = this.options.compilerArgs
+    compilerArgs += listOf(
+            "-XDcompilePolicy=simple", // Workaround for b/36098770
+
+            // Enforce the following checks.
+            "-Xep:RestrictTo:OFF",
+            "-Xep:ParameterNotNullable:ERROR",
+            "-Xep:MissingOverride:ERROR",
+            "-Xep:JdkObsolete:ERROR",
+            "-Xep:NarrowingCompoundAssignment:ERROR",
+            "-Xep:ClassNewInstance:ERROR",
+            "-Xep:ClassCanBeStatic:ERROR",
+            "-Xep:SynchronizeOnNonFinalField:ERROR",
+            "-Xep:OperatorPrecedence:ERROR",
+            "-Xep:IntLongMath:ERROR",
+            "-Xep:MissingFail:ERROR",
+
+            // Nullaway
+            "-XepIgnoreUnknownCheckNames", // https://github.com/uber/NullAway/issues/25
+            "-Xep:NullAway:ERROR",
+            "-XepOpt:NullAway:AnnotatedPackages=android.arch,android.support"
+    )
+}
diff --git a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt b/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
index 8f3fdc5..a9cb293 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/android/support/SupportAndroidLibraryPlugin.kt
@@ -143,30 +143,7 @@
         library.libraryVariants.all { libraryVariant ->
             if (libraryVariant.getBuildType().getName().equals("errorProne")) {
                 @Suppress("DEPRECATION")
-                libraryVariant.getJavaCompile().toolChain = toolChain
-
-                @Suppress("DEPRECATION")
-                val compilerArgs = libraryVariant.javaCompile.options.compilerArgs
-                compilerArgs += arrayListOf(
-                        "-XDcompilePolicy=simple", // Workaround for b/36098770
-
-                        // Enforce the following checks.
-                        "-Xep:RestrictTo:OFF",
-                        "-Xep:ParameterNotNullable:ERROR",
-                        "-Xep:MissingOverride:ERROR",
-                        "-Xep:JdkObsolete:ERROR",
-                        "-Xep:NarrowingCompoundAssignment:ERROR",
-                        "-Xep:ClassNewInstance:ERROR",
-                        "-Xep:ClassCanBeStatic:ERROR",
-                        "-Xep:SynchronizeOnNonFinalField:ERROR",
-                        "-Xep:OperatorPrecedence:ERROR",
-                        "-Xep:IntLongMath:ERROR",
-
-                        // Nullaway
-                        "-XepIgnoreUnknownCheckNames", // https://github.com/uber/NullAway/issues/25
-                        "-Xep:NullAway:ERROR",
-                        "-XepOpt:NullAway:AnnotatedPackages=android.arch,android.support"
-                )
+                libraryVariant.javaCompile.configureWithErrorProne(toolChain)
             }
         }
     }
@@ -207,4 +184,4 @@
     if (baseline.exists()) {
         lintOptions.baseline(baseline)
     }
-}
\ No newline at end of file
+}
diff --git a/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt b/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt
index 0701433..cc2f985 100644
--- a/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt
+++ b/buildSrc/src/main/kotlin/android/support/SupportJavaLibraryPlugin.kt
@@ -16,10 +16,13 @@
 
 package android.support
 
+import net.ltgt.gradle.errorprone.ErrorProneBasePlugin
+import net.ltgt.gradle.errorprone.ErrorProneToolChain
 import org.gradle.api.JavaVersion
 import org.gradle.api.Plugin
 import org.gradle.api.Project
 import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.compile.JavaCompile
 
 /**
  * Support java library specific plugin that sets common configurations needed for
@@ -44,6 +47,11 @@
             }
         }
 
+        project.apply(mapOf("plugin" to ErrorProneBasePlugin::class.java))
+        val toolChain = ErrorProneToolChain.create(project)
+        val compileTasks = project.tasks.withType(JavaCompile::class.java)
+        compileTasks.all { it.configureWithErrorProne(toolChain) }
+
         setUpSourceJarTaskForJavaProject(project)
     }
-}
\ No newline at end of file
+}
diff --git a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
index 529a9f0..97706a0 100644
--- a/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
+++ b/car/src/androidTest/java/androidx/car/widget/TextListItemTest.java
@@ -38,6 +38,7 @@
 import android.support.test.rule.ActivityTestRule;
 import android.support.test.runner.AndroidJUnit4;
 import android.support.v7.widget.LinearLayoutManager;
+import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.RelativeLayout;
@@ -587,6 +588,43 @@
     }
 
     @Test
+    public void testRevertingViewBinder() throws Throwable {
+        TextListItem item0 = new TextListItem(mActivity);
+        item0.setBody("one item");
+        item0.addViewBinder(
+                (viewHolder) -> viewHolder.getBody().setEllipsize(TextUtils.TruncateAt.END),
+                (viewHolder -> viewHolder.getBody().setEllipsize(null)));
+
+        List<TextListItem> items = Arrays.asList(item0);
+        setupPagedListView(items);
+
+        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
+
+        // Bind view holder to a new item - the customization made by item0 should be reverted.
+        TextListItem item1 = new TextListItem(mActivity);
+        item1.setBody("new item");
+        mActivityRule.runOnUiThread(() -> item1.bind(viewHolder));
+
+        assertThat(viewHolder.getBody().getEllipsize(), is(equalTo(null)));
+    }
+
+    @Test
+    public void testRemovingViewBinder() {
+        TextListItem item0 = new TextListItem(mActivity);
+        item0.setBody("one item");
+        ListItem.ViewBinder<TextListItem.ViewHolder> binder =
+                (viewHolder) -> viewHolder.getTitle().setEllipsize(TextUtils.TruncateAt.END);
+        item0.addViewBinder(binder);
+
+        assertTrue(item0.removeViewBinder(binder));
+
+        List<TextListItem> items = Arrays.asList(item0);
+        setupPagedListView(items);
+
+        assertThat(getViewHolderAtPosition(0).getBody().getEllipsize(), is(equalTo(null)));
+    }
+
+    @Test
     public void testSettingTitleOrBodyAsPrimaryText() {
         // Create 2 items, one with Title as primary (default) and one with Body.
         // The primary text, regardless of view, should have consistent look (as primary).
diff --git a/car/src/main/java/androidx/car/widget/ListItem.java b/car/src/main/java/androidx/car/widget/ListItem.java
index 74ff2dd..97e8fca 100644
--- a/car/src/main/java/androidx/car/widget/ListItem.java
+++ b/car/src/main/java/androidx/car/widget/ListItem.java
@@ -1,15 +1,19 @@
 package androidx.car.widget;
 
+import android.support.annotation.Nullable;
 import android.support.v7.widget.RecyclerView;
+import android.view.View;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.function.Function;
 
 /**
  * Definition of items that can be inserted into {@link ListItemAdapter}.
  *
- * @param <VH> ViewHolder.
+ * @param <VH> ViewHolder that extends {@link ListItem.ViewHolder}.
  */
-public abstract class ListItem<VH extends RecyclerView.ViewHolder> {
+public abstract class ListItem<VH extends ListItem.ViewHolder> {
 
     // Whether the item should calculate view layout params. This usually happens when the item is
     // updated after bind() is called. Calling bind() resets to false.
@@ -18,6 +22,11 @@
     // Tag for indicating whether to hide the divider.
     private boolean mHideDivider;
 
+    private final List<ViewBinder<VH>> mCustomBinders = new ArrayList<>();
+    // Stores ViewBinders to revert customization. Does not guarantee to 1:1 match ViewBinders
+    // in mCustomerBinders.
+    private final List<ViewBinder<VH>> mCustomBinderCleanUps = new ArrayList<>();
+
     /**
      * Classes that extends {@code ListItem} should register its view type in
      * {@link ListItemAdapter#registerListItemViewType(int, int, Function)}.
@@ -29,27 +38,44 @@
     /**
      * Called when ListItem is bound to its ViewHolder.
      */
-    public abstract void bind(VH viewHolder);
+    final void bind(VH viewHolder) {
+        // Attempt to clean up custom view binder from previous item (if any).
+        // Then save the clean up binders for next item.
+        viewHolder.cleanUp();
+        for (ViewBinder cleanUp : mCustomBinderCleanUps) {
+            viewHolder.addCleanUp(cleanUp);
+        }
+
+        if (isDirty()) {
+            resolveDirtyState();
+            markClean();
+        }
+        onBind(viewHolder);
+
+        // Custom view binders are applied after view layout.
+        for (ViewBinder<VH> binder: mCustomBinders) {
+            binder.bind(viewHolder);
+        }
+    }
 
     /**
-     * Marks this item so that sub-views in ViewHolder will need layout params re-calculated
-     * in next bind().
+     * Marks this item as dirty so {@link #resolveDirtyState()} is required in next bind() call.
      *
-     * This method should be called in each setter.
+     * <p>This method should be called in each setter.
      */
     protected void markDirty() {
         mDirty = true;
     }
 
     /**
-     * Marks this item as not dirty - no need to calculate sub-view layout params in bind().
+     * Marks this item as not dirty. No need to call {@link #resolveDirtyState()} in next bind().
      */
     protected void markClean() {
         mDirty = false;
     }
 
     /**
-     * @return {@code true} if this item needs to calculate sub-view layout params.
+     * @return {@code true} if next bind() should call {@link #resolveDirtyState()}.
      */
     protected boolean isDirty() {
         return mDirty;
@@ -75,16 +101,118 @@
         return mHideDivider;
     };
 
+
+    /**
+     * Does the work that moves the ListItem from dirty state to clean state, i.e. the work required
+     * the first time this ListItem {@code bind}s to {@link ListItem.ViewHolder}.
+     * This method will transition ListItem to clean state. ListItem in clean state should move to
+     * dirty state when it is modified by calling {@link #markDirty()}.
+     */
+    protected abstract void resolveDirtyState();
+
+    /**
+     * Binds this ListItem to {@code viewHolder} by applying data in ListItem to sub-views.
+     * Assume {@link ViewHolder#cleanUp()} has already been invoked.
+     */
+    protected abstract void onBind(VH viewHolder);
+
+    /**
+     * Same as {@link #addViewBinder(ViewBinder, ViewBinder)} when {@code cleanUp} ViewBinder
+     * is null.
+     *
+     * @param binder to interact with subviews in {@code ViewHolder}.
+     *
+     * @see #addViewBinder(ViewBinder, ViewBinder)
+     */
+    public final void addViewBinder(ViewBinder<VH> binder) {
+        addViewBinder(binder, null);
+    }
+
+    /**
+     * Adds {@link ViewBinder} to interact with sub-views in {@link ViewHolder}. These ViewBinders
+     * will always be applied after {@link #onBind(ViewHolder)}.
+     *
+     * <p>To interact with a foobar sub-view in {@code ViewHolder}, make sure to first set its
+     * visibility, or call setFoobar() setter method.
+     *
+     * <p>Example:
+     * <pre>
+     * {@code
+     * TextListItem item = new TextListItem(context);
+     * item.setTitle("title");
+     * item.addViewBinder((viewHolder) -> {
+     *     viewHolder.getTitle().doFoobar();
+     * }, (viewHolder) -> {
+     *     viewHolder.getTitle().revertFoobar();
+     * });
+     * }
+     * </pre>
+     *
+     * @params binder to interact with subviews in {@code ViewHolder}.
+     * @params cleanUp view binder to revert the effect of {@code binder}. cleanUp binders will be
+     *                 stored in {@link ListItem.ViewHolder} and should be invoked via
+     *                 {@link ViewHolder#cleanUp()} before {@code ViewHolder} is recycled.
+     *                 This is to avoid changed made to ViewHolder lingers around when ViewHolder is
+     *                 recycled. Pass in null to skip.
+     */
+    public final void addViewBinder(ViewBinder<VH> binder, @Nullable ViewBinder<VH> cleanUp) {
+        mCustomBinders.add(binder);
+        if (cleanUp != null) {
+            mCustomBinderCleanUps.add(cleanUp);
+        }
+        markDirty();
+    }
+
+    /**
+     * Removes the first occurrence of the specified item.
+     *
+     * @param binder to be removed.
+     * @return {@code true} if {@code binder} exists. {@code false} otherwise.
+     */
+    public boolean removeViewBinder(ViewBinder<VH> binder) {
+        return mCustomBinders.remove(binder);
+    }
+
     /**
      * Functional interface to provide a way to interact with views in {@code ViewHolder}.
      * {@code ListItem} calls all added ViewBinders when it {@code bind}s to {@code ViewHolder}.
      *
-     * @param <VH> extends {@link RecyclerView.ViewHolder}.
+     * @param <VH> class that extends {@link RecyclerView.ViewHolder}.
      */
-    public interface ViewBinder<VH extends RecyclerView.ViewHolder> {
+    public interface ViewBinder<VH> {
         /**
          * Provides a way to interact with views in view holder.
          */
         void bind(VH viewHolder);
     }
+
+    /**
+     * ViewHolder that supports {@link ViewBinder}.
+     */
+    public abstract static class ViewHolder extends RecyclerView.ViewHolder {
+        private final List<ViewBinder> mCleanUps = new ArrayList<>();
+
+        public ViewHolder(View itemView) {
+            super(itemView);
+        }
+
+        /**
+         * Removes customization from previous ListItem. Intended to be used when this ViewHolder is
+         * bound to a ListItem.
+         */
+        public final void cleanUp() {
+            for (ViewBinder binder : mCleanUps) {
+                binder.bind(this);
+            }
+        }
+
+        /**
+         * Stores clean up ViewBinders that will be called in {@code cleanUp()}.
+         */
+        public final void addCleanUp(@Nullable ViewBinder<ViewHolder> cleanUp) {
+            if (cleanUp != null) {
+                mCleanUps.add(cleanUp);
+            }
+        }
+    }
 }
diff --git a/car/src/main/java/androidx/car/widget/ListItemAdapter.java b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
index 8dedbf5..78b1d6e 100644
--- a/car/src/main/java/androidx/car/widget/ListItemAdapter.java
+++ b/car/src/main/java/androidx/car/widget/ListItemAdapter.java
@@ -43,7 +43,7 @@
  *
  */
 public class ListItemAdapter extends
-        RecyclerView.Adapter<RecyclerView.ViewHolder> implements PagedListView.ItemCap,
+        RecyclerView.Adapter<ListItem.ViewHolder> implements PagedListView.ItemCap,
         PagedListView.DividerVisibilityManager {
 
     /**
@@ -74,7 +74,7 @@
     static final int LIST_ITEM_TYPE_SEEKBAR = 2;
 
     private final SparseIntArray mViewHolderLayoutResIds = new SparseIntArray();
-    private final SparseArray<Function<View, RecyclerView.ViewHolder>> mViewHolderCreator =
+    private final SparseArray<Function<View, ListItem.ViewHolder>> mViewHolderCreator =
             new SparseArray<>();
 
     /**
@@ -90,7 +90,7 @@
      * @param function function to create ViewHolder for {@code viewType}.
      */
     public void registerListItemViewType(int viewType, @LayoutRes int layoutResId,
-            Function<View, RecyclerView.ViewHolder> function) {
+            Function<View, ListItem.ViewHolder> function) {
         if (mViewHolderLayoutResIds.get(viewType) != 0
                 || mViewHolderCreator.get(viewType) != null) {
             throw new IllegalArgumentException("View type is already registered.");
@@ -121,7 +121,7 @@
     }
 
     @Override
-    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+    public ListItem.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         if (mViewHolderLayoutResIds.get(viewType) == 0
                 || mViewHolderCreator.get(viewType) == null) {
             throw new IllegalArgumentException("Unregistered view type.");
@@ -175,7 +175,7 @@
     }
 
     @Override
-    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
+    public void onBindViewHolder(ListItem.ViewHolder holder, int position) {
         ListItem item = mItemProvider.get(position);
         item.bind(holder);
 
diff --git a/car/src/main/java/androidx/car/widget/PagedSnapHelper.java b/car/src/main/java/androidx/car/widget/PagedSnapHelper.java
index 9bf1fb6..610894c 100644
--- a/car/src/main/java/androidx/car/widget/PagedSnapHelper.java
+++ b/car/src/main/java/androidx/car/widget/PagedSnapHelper.java
@@ -29,6 +29,12 @@
  * the start of the attached {@link RecyclerView}. The start of the view is defined as the top
  * if the RecyclerView is scrolling vertically; it is defined as the left (or right if RTL) if the
  * RecyclerView is scrolling horizontally.
+ *
+ * <p>Snapping may be disabled for views whose height is greater than that of the
+ * {@code RecyclerView} that contains them. In this case, the view will only be snapped to when it
+ * is first encountered. Otherwise, the user will be allowed to scroll freely through that view
+ * when it appears in the list. The snapping behavior will resume when the large view is scrolled
+ * off-screen.
  */
 public class PagedSnapHelper extends LinearSnapHelper {
     /**
@@ -41,11 +47,8 @@
     private RecyclerView mRecyclerView;
 
     // Orientation helpers are lazily created per LayoutManager.
-    @Nullable
-    private OrientationHelper mVerticalHelper;
-
-    @Nullable
-    private OrientationHelper mHorizontalHelper;
+    @Nullable private OrientationHelper mVerticalHelper;
+    @Nullable private OrientationHelper mHorizontalHelper;
 
     public PagedSnapHelper(Context context) {
         mSmoothScroller = new PagedSmoothScroller(context);
@@ -85,15 +88,14 @@
             return null;
         }
 
+        OrientationHelper orientationHelper = getOrientationHelper(layoutManager);
+
         // If there's only one child, then that will be the snap target.
         if (childCount == 1) {
-            return layoutManager.getChildAt(0);
+            View firstChild = layoutManager.getChildAt(0);
+            return isValidSnapView(firstChild, orientationHelper) ? firstChild : null;
         }
 
-        OrientationHelper orientationHelper = layoutManager.canScrollVertically()
-                ? getVerticalHelper(layoutManager)
-                : getHorizontalHelper(layoutManager);
-
         View lastVisibleChild = layoutManager.getChildAt(childCount - 1);
 
         // Check if the last child visible is the last item in the list.
@@ -108,6 +110,7 @@
         int closestDistanceToStart = Integer.MAX_VALUE;
         float closestPercentageVisible = 0.f;
 
+        // Iterate to find the child closest to the top and more than half way visible.
         for (int i = 0; i < childCount; i++) {
             View child = layoutManager.getChildAt(i);
             int startOffset = orientationHelper.getDecoratedStart(child);
@@ -115,8 +118,6 @@
             if (Math.abs(startOffset) < closestDistanceToStart) {
                 float percentageVisible = getPercentageVisible(child, orientationHelper);
 
-                // Only snap to the child that is closest to the top and is more than
-                // half-way visible.
                 if (percentageVisible > VIEW_VISIBLE_THRESHOLD
                         && percentageVisible > closestPercentageVisible) {
                     closestDistanceToStart = startOffset;
@@ -126,11 +127,43 @@
             }
         }
 
-        // Snap to the last child in the list if it's the last item in the list, and it's more
-        // visible than the closest item to the top of the list.
-        return (lastItemVisible && lastItemPercentageVisible > closestPercentageVisible)
-                ? lastVisibleChild
-                : closestChild;
+        View childToReturn = closestChild;
+
+        // If closestChild is null, then that means we were unable to find a closest child that
+        // is over the VIEW_VISIBLE_THRESHOLD. This could happen if the views are larger than
+        // the given area. In this case, consider returning the lastVisibleChild so that the screen
+        // scrolls. Also, check if the last item should be displayed anyway if it is mostly visible.
+        if ((childToReturn == null
+                || (lastItemVisible && lastItemPercentageVisible > closestPercentageVisible))) {
+            childToReturn = lastVisibleChild;
+        }
+
+        // Return null if the childToReturn is not valid. This allows the user to scroll freely
+        // with no snapping. This can allow them to see the entire view.
+        return isValidSnapView(childToReturn, orientationHelper) ? childToReturn : null;
+    }
+
+    /**
+     * Returns whether or not the given View is a valid snapping view. A view is considered valid
+     * for snapping if:
+     *
+     * <ol>
+     *     <li>It can fit entirely within the height of the RecyclerView it is contained within.
+     *     <li>Or the start of the view is not above the start of the RecyclerView.
+     * </ol>
+     *
+     * <p>If the view is larger than the RecyclerView, then it might not want to be snapped to
+     * to allow the user to scroll and see the rest of the View. Likewise, if the start of the
+     * view is above the start of the RecyclerView, it implies that the user has already scrolled
+     * past it and might want to see the rest of it. Thus, the view should not be snapped to.
+     *
+     * @param view The view to determine the snapping potential.
+     * @param helper The {@link OrientationHelper} associated with the current RecyclerView.
+     * @return {@code true} if the given view is a valid snapping view; {@code false} otherwise.
+     */
+    private boolean isValidSnapView(View view, OrientationHelper helper) {
+        return helper.getDecoratedMeasurement(view) <= helper.getLayoutManager().getHeight()
+                || helper.getDecoratedStart(view) >= 0;
     }
 
     /**
@@ -210,12 +243,19 @@
 
         int lastChildPosition = isAtEnd(layoutManager) ? 0 : layoutManager.getChildCount() - 1;
 
-        // The max and min distance is the total height of the RecyclerView minus the height of
-        // the last child. This ensures that each scroll will never scroll more than a single
-        // page on the RecyclerView. That is, the max scroll will make the last child the
-        // first child and vice versa when scrolling the opposite way.
-        int maxDistance = layoutManager.getHeight() - layoutManager.getDecoratedMeasuredHeight(
-                layoutManager.getChildAt(lastChildPosition));
+        OrientationHelper orientationHelper = getOrientationHelper(layoutManager);
+        View lastChild = layoutManager.getChildAt(lastChildPosition);
+        float percentageVisible = getPercentageVisible(lastChild, orientationHelper);
+
+        int maxDistance = layoutManager.getHeight();
+        if (percentageVisible > 0.f) {
+            // The max and min distance is the total height of the RecyclerView minus the height of
+            // the last child. This ensures that each scroll will never scroll more than a single
+            // page on the RecyclerView. That is, the max scroll will make the last child the
+            // first child and vice versa when scrolling the opposite way.
+            maxDistance -= layoutManager.getDecoratedMeasuredHeight(lastChild);
+        }
+
         int minDistance = -maxDistance;
 
         outDist[0] = clamp(outDist[0], minDistance, maxDistance);
@@ -255,6 +295,18 @@
                 && layoutManager.getDecoratedBottom(lastVisibleChild) <= layoutManager.getHeight();
     }
 
+    /**
+     * Returns an {@link OrientationHelper} that corresponds to the current scroll direction of
+     * the given {@link android.support.v7.widget.RecyclerView.LayoutManager}.
+     */
+    @NonNull
+    private OrientationHelper getOrientationHelper(
+            @NonNull RecyclerView.LayoutManager layoutManager) {
+        return layoutManager.canScrollVertically()
+                ? getVerticalHelper(layoutManager)
+                : getHorizontalHelper(layoutManager);
+    }
+
     @NonNull
     private OrientationHelper getVerticalHelper(@NonNull RecyclerView.LayoutManager layoutManager) {
         if (mVerticalHelper == null || mVerticalHelper.getLayoutManager() != layoutManager) {
diff --git a/car/src/main/java/androidx/car/widget/SeekbarListItem.java b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
index 54c37ef..2e98817 100644
--- a/car/src/main/java/androidx/car/widget/SeekbarListItem.java
+++ b/car/src/main/java/androidx/car/widget/SeekbarListItem.java
@@ -24,7 +24,6 @@
 import android.support.annotation.IdRes;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
 import android.text.TextUtils;
 import android.view.View;
 import android.view.ViewGroup;
@@ -93,8 +92,6 @@
     private final Context mContext;
 
     private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
-    // Store custom binders separately so they will bind after binders created by setters.
-    private final List<ViewBinder<ViewHolder>> mCustomBinders = new ArrayList<>();
 
     @PrimaryActionType private int mPrimaryActionType = PRIMARY_ACTION_TYPE_NO_ICON;
     private int mPrimaryActionIconResId;
@@ -156,33 +153,32 @@
     }
 
     /**
-     * Applies all {@link ViewBinder} to {@code ViewHolder}.
+     * Calculates the layout params for views in {@link ViewHolder}.
      */
     @Override
-    public void bind(ViewHolder viewHolder) {
-        if (isDirty()) {
-            mBinders.clear();
+    protected void resolveDirtyState() {
+        mBinders.clear();
 
-            // Create binders that adjust layout params of each view.
-            setItemLayoutHeight();
-            setPrimaryAction();
-            setSeekBarAndText();
-            setSupplementalAction();
+        // Create binders that adjust layout params of each view.
+        setItemLayoutHeight();
+        setPrimaryAction();
+        setSeekBarAndText();
+        setSupplementalAction();
+    }
 
-            // Custom view binders are always applied after the one created by this class.
-            mBinders.addAll(mCustomBinders);
-
-            markClean();
-        }
-
+    /**
+     * Hides all views in {@link ViewHolder} then applies ViewBinders to adjust view layout params.
+     */
+    @Override
+    protected void onBind(ViewHolder viewHolder) {
         // Hide all subviews then apply view binders to adjust subviews.
-        setSubViewsGone(viewHolder);
+        hideSubViews(viewHolder);
         for (ViewBinder binder : mBinders) {
             binder.bind(viewHolder);
         }
     }
 
-    private void setSubViewsGone(ViewHolder vh) {
+    private void hideSubViews(ViewHolder vh) {
         View[] subviews = new View[] {
                 vh.getPrimaryIcon(),
                 // SeekBar is always visible.
@@ -499,31 +495,9 @@
     }
 
     /**
-     * Adds {@code ViewBinder} to interact with sub-views in {@link ViewHolder}. These ViewBinders
-     * will always be applied after other {@code setFoobar} methods have bound.
-     *
-     * <p>Make sure to call setFoobar() method on the intended sub-view first.
-     *
-     * <p>Example:
-     * <pre>
-     * {@code
-     * SeekbarListItem item = new SeebarListItem(context);
-     * item.setPrimaryActionIcon(R.drawable.icon);
-     * item.addViewBinder((viewHolder) -> {
-     *     viewHolder.getPrimaryIcon().doMoreStuff();
-     * });
-     * }
-     * </pre>
-     */
-    public void addViewBinder(ViewBinder<ViewHolder> viewBinder) {
-        mCustomBinders.add(viewBinder);
-        markDirty();
-    }
-
-    /**
      * Holds views of SeekbarListItem.
      */
-    public static class ViewHolder extends RecyclerView.ViewHolder {
+    public static class ViewHolder extends ListItem.ViewHolder {
 
         private RelativeLayout mContainerLayout;
 
diff --git a/car/src/main/java/androidx/car/widget/TextListItem.java b/car/src/main/java/androidx/car/widget/TextListItem.java
index 71b6c96..871c14b 100644
--- a/car/src/main/java/androidx/car/widget/TextListItem.java
+++ b/car/src/main/java/androidx/car/widget/TextListItem.java
@@ -101,8 +101,6 @@
     private final Context mContext;
 
     private final List<ViewBinder<ViewHolder>> mBinders = new ArrayList<>();
-    // Store custom binders separately so they will bind after binders created by setters.
-    private final List<ViewBinder<ViewHolder>> mCustomBinders = new ArrayList<>();
 
     private View.OnClickListener mOnClickListener;
 
@@ -151,34 +149,32 @@
     }
 
     /**
-     * Applies all {@link ViewBinder} to {@code ViewHolder}.
+     * Calculates the layout params for views in {@link ViewHolder}.
      */
     @Override
-    public void bind(ViewHolder viewHolder) {
-        if (isDirty()) {
-            mBinders.clear();
+    protected void resolveDirtyState() {
+        mBinders.clear();
 
-            // Create binders that adjust layout params of each view.
-            setItemLayoutHeight();
-            setPrimaryAction();
-            setText();
-            setSupplementalActions();
-            setOnClickListener();
+        // Create binders that adjust layout params of each view.
+        setItemLayoutHeight();
+        setPrimaryAction();
+        setText();
+        setSupplementalActions();
+        setOnClickListener();
+    }
 
-            // Custom view binders are always applied after the one created by this class.
-            mBinders.addAll(mCustomBinders);
-
-            markClean();
-        }
-
-        // Hide all subviews then apply view binders to adjust subviews.
-        setAllSubViewsGone(viewHolder);
+    /**
+     * Hides all views in {@link ViewHolder} then applies ViewBinders to adjust view layout params.
+     */
+    @Override
+    public void onBind(ViewHolder viewHolder) {
+        hideSubViews(viewHolder);
         for (ViewBinder binder : mBinders) {
             binder.bind(viewHolder);
         }
     }
 
-    void setAllSubViewsGone(ViewHolder vh) {
+    private void hideSubViews(ViewHolder vh) {
         View[] subviews = new View[] {
                 vh.getPrimaryIcon(),
                 vh.getTitle(), vh.getBody(),
@@ -709,31 +705,9 @@
     }
 
     /**
-     * Adds {@link ViewBinder} to interact with sub-views in {@link ViewHolder}. These ViewBinders
-     * will always bind after other {@code setFoobar} methods have bound.
-     *
-     * <p>Make sure to call setFoobar() method on the intended sub-view first.
-     *
-     * <p>Example:
-     * <pre>
-     * {@code
-     * TextListItem item = new TextListItem(context);
-     * item.setTitle("title");
-     * item.addViewBinder((viewHolder) -> {
-     *     viewHolder.getTitle().doMoreStuff();
-     * });
-     * }
-     * </pre>
-     */
-    public void addViewBinder(ViewBinder<ViewHolder> binder) {
-        mCustomBinders.add(binder);
-        markDirty();
-    }
-
-    /**
      * Holds views of TextListItem.
      */
-    public static class ViewHolder extends RecyclerView.ViewHolder {
+    public static class ViewHolder extends ListItem.ViewHolder {
 
         private RelativeLayout mContainerLayout;
 
diff --git a/compat/api/current.txt b/compat/api/current.txt
index f4423f1..f298c72 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -689,13 +689,13 @@
     method public static int checkSelfPermission(android.content.Context, java.lang.String);
     method public static android.content.Context createDeviceProtectedStorageContext(android.content.Context);
     method public static java.io.File getCodeCacheDir(android.content.Context);
-    method public static final int getColor(android.content.Context, int);
-    method public static final android.content.res.ColorStateList getColorStateList(android.content.Context, int);
+    method public static int getColor(android.content.Context, int);
+    method public static android.content.res.ColorStateList getColorStateList(android.content.Context, int);
     method public static java.io.File getDataDir(android.content.Context);
-    method public static final android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
+    method public static android.graphics.drawable.Drawable getDrawable(android.content.Context, int);
     method public static java.io.File[] getExternalCacheDirs(android.content.Context);
     method public static java.io.File[] getExternalFilesDirs(android.content.Context, java.lang.String);
-    method public static final java.io.File getNoBackupFilesDir(android.content.Context);
+    method public static java.io.File getNoBackupFilesDir(android.content.Context);
     method public static java.io.File[] getObbDirs(android.content.Context);
     method public static boolean isDeviceProtectedStorage(android.content.Context);
     method public static boolean startActivities(android.content.Context, android.content.Intent[]);
@@ -1103,14 +1103,14 @@
 package android.support.v4.text.util {
 
   public final class LinkifyCompat {
-    method public static final boolean addLinks(android.text.Spannable, int);
-    method public static final boolean addLinks(android.widget.TextView, int);
-    method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
-    method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
-    method public static final void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
-    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
-    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
-    method public static final boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static boolean addLinks(android.text.Spannable, int);
+    method public static boolean addLinks(android.widget.TextView, int);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static void addLinks(android.widget.TextView, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
+    method public static boolean addLinks(android.text.Spannable, java.util.regex.Pattern, java.lang.String, java.lang.String[], android.text.util.Linkify.MatchFilter, android.text.util.Linkify.TransformFilter);
   }
 
 }
diff --git a/compat/src/main/java/android/support/v4/app/ActivityCompat.java b/compat/src/main/java/android/support/v4/app/ActivityCompat.java
index 333871a..5599360 100644
--- a/compat/src/main/java/android/support/v4/app/ActivityCompat.java
+++ b/compat/src/main/java/android/support/v4/app/ActivityCompat.java
@@ -575,7 +575,7 @@
 
         protected SharedElementCallback mCallback;
 
-        public SharedElementCallback21Impl(SharedElementCallback callback) {
+        SharedElementCallback21Impl(SharedElementCallback callback) {
             mCallback = callback;
         }
 
@@ -618,7 +618,7 @@
 
     @RequiresApi(23)
     private static class SharedElementCallback23Impl extends SharedElementCallback21Impl {
-        public SharedElementCallback23Impl(SharedElementCallback callback) {
+        SharedElementCallback23Impl(SharedElementCallback callback) {
             super(callback);
         }
 
diff --git a/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java b/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
index 7099cb9..8ddb5c3 100644
--- a/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
+++ b/compat/src/main/java/android/support/v4/app/NotificationManagerCompat.java
@@ -317,7 +317,7 @@
                 new HashMap<ComponentName, ListenerRecord>();
         private Set<String> mCachedEnabledPackages = new HashSet<String>();
 
-        public SideChannelManager(Context context) {
+        SideChannelManager(Context context) {
             mContext = context;
             mHandlerThread = new HandlerThread("NotificationManagerCompat");
             mHandlerThread.start();
@@ -554,17 +554,17 @@
 
         /** A per-side-channel-service listener state record */
         private static class ListenerRecord {
-            public final ComponentName componentName;
+            final ComponentName componentName;
             /** Whether the service is currently bound to. */
-            public boolean bound = false;
+            boolean bound = false;
             /** The service stub provided by onServiceConnected */
-            public INotificationSideChannel service;
+            INotificationSideChannel service;
             /** Queue of pending tasks to send to this listener service */
-            public ArrayDeque<Task> taskQueue = new ArrayDeque<>();
+            ArrayDeque<Task> taskQueue = new ArrayDeque<>();
             /** Number of retries attempted while connecting to this listener service */
-            public int retryCount = 0;
+            int retryCount = 0;
 
-            public ListenerRecord(ComponentName componentName) {
+            ListenerRecord(ComponentName componentName) {
                 this.componentName = componentName;
             }
         }
diff --git a/compat/src/main/java/android/support/v4/content/ContextCompat.java b/compat/src/main/java/android/support/v4/content/ContextCompat.java
index 8a4fd7a..6aba339 100644
--- a/compat/src/main/java/android/support/v4/content/ContextCompat.java
+++ b/compat/src/main/java/android/support/v4/content/ContextCompat.java
@@ -353,7 +353,7 @@
      * @return Drawable An object that can be used to draw this resource.
      */
     @Nullable
-    public static final Drawable getDrawable(@NonNull Context context, @DrawableRes int id) {
+    public static Drawable getDrawable(@NonNull Context context, @DrawableRes int id) {
         if (Build.VERSION.SDK_INT >= 21) {
             return context.getDrawable(id);
         } else if (Build.VERSION.SDK_INT >= 16) {
@@ -390,7 +390,7 @@
      *         does not exist.
      */
     @Nullable
-    public static final ColorStateList getColorStateList(@NonNull Context context,
+    public static ColorStateList getColorStateList(@NonNull Context context,
             @ColorRes int id) {
         if (Build.VERSION.SDK_INT >= 23) {
             return context.getColorStateList(id);
@@ -413,7 +413,7 @@
      *         does not exist.
      */
     @ColorInt
-    public static final int getColor(@NonNull Context context, @ColorRes int id) {
+    public static int getColor(@NonNull Context context, @ColorRes int id) {
         if (Build.VERSION.SDK_INT >= 23) {
             return context.getColor(id);
         } else {
@@ -454,7 +454,7 @@
      * @see android.content.Context#getFilesDir()
      */
     @Nullable
-    public static final File getNoBackupFilesDir(@NonNull Context context) {
+    public static File getNoBackupFilesDir(@NonNull Context context) {
         if (Build.VERSION.SDK_INT >= 21) {
             return context.getNoBackupFilesDir();
         } else {
diff --git a/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java b/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
index 39acf68..7fff1d0 100644
--- a/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
+++ b/compat/src/main/java/android/support/v4/provider/FontsContractCompat.java
@@ -220,7 +220,7 @@
      * @hide
      **/
     @RestrictTo(LIBRARY_GROUP)
-    public static final void resetCache() {
+    public static void resetCache() {
         sTypefaceCache.evictAll();
     }
 
diff --git a/compat/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java b/compat/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java
index d55ae8a..10c6b76 100644
--- a/compat/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java
+++ b/compat/src/main/java/android/support/v4/text/TextDirectionHeuristicsCompat.java
@@ -111,7 +111,7 @@
     private static abstract class TextDirectionHeuristicImpl implements TextDirectionHeuristicCompat {
         private final TextDirectionAlgorithm mAlgorithm;
 
-        public TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
+        TextDirectionHeuristicImpl(TextDirectionAlgorithm algorithm) {
             mAlgorithm = algorithm;
         }
 
@@ -166,7 +166,7 @@
     /**
      * Interface for an algorithm to guess the direction of a paragraph of text.
      */
-    private static interface TextDirectionAlgorithm {
+    private interface TextDirectionAlgorithm {
         /**
          * Returns whether the range of text is RTL according to the algorithm.
          */
@@ -190,7 +190,7 @@
         private FirstStrong() {
         }
 
-        public static final FirstStrong INSTANCE = new FirstStrong();
+        static final FirstStrong INSTANCE = new FirstStrong();
     }
 
     /**
@@ -232,8 +232,8 @@
             this.mLookForRtl = lookForRtl;
         }
 
-        public static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
-        public static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
+        static final AnyStrong INSTANCE_RTL = new AnyStrong(true);
+        static final AnyStrong INSTANCE_LTR = new AnyStrong(false);
     }
 
     /**
@@ -241,7 +241,7 @@
      */
     private static class TextDirectionHeuristicLocale extends TextDirectionHeuristicImpl {
 
-        public TextDirectionHeuristicLocale() {
+        TextDirectionHeuristicLocale() {
             super(null);
         }
 
@@ -251,7 +251,7 @@
             return (dir == ViewCompat.LAYOUT_DIRECTION_RTL);
         }
 
-        public static final TextDirectionHeuristicLocale INSTANCE =
+        static final TextDirectionHeuristicLocale INSTANCE =
                 new TextDirectionHeuristicLocale();
     }
 
diff --git a/compat/src/main/java/android/support/v4/text/util/LinkifyCompat.java b/compat/src/main/java/android/support/v4/text/util/LinkifyCompat.java
index b7bf352..efd4c85 100644
--- a/compat/src/main/java/android/support/v4/text/util/LinkifyCompat.java
+++ b/compat/src/main/java/android/support/v4/text/util/LinkifyCompat.java
@@ -56,7 +56,7 @@
 
     private static final Comparator<LinkSpec>  COMPARATOR = new Comparator<LinkSpec>() {
         @Override
-        public final int compare(LinkSpec a, LinkSpec b) {
+        public int compare(LinkSpec a, LinkSpec b) {
             if (a.start < b.start) {
                 return -1;
             }
@@ -96,7 +96,7 @@
      *
      *  @return True if at least one link is found and applied.
      */
-    public static final boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
+    public static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
         if (Build.VERSION.SDK_INT >= 27) {
             return Linkify.addLinks(text, mask);
         }
@@ -160,7 +160,7 @@
      *
      *  @return True if at least one link is found and applied.
      */
-    public static final boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
+    public static boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
         if (Build.VERSION.SDK_INT >= 26) {
             return Linkify.addLinks(text, mask);
         }
@@ -202,7 +202,7 @@
      *  @param scheme       URL scheme string (eg <code>http://</code>) to be
      *                      prepended to the links that do not start with this scheme.
      */
-    public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
+    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme) {
         if (Build.VERSION.SDK_INT >= 26) {
             Linkify.addLinks(text, pattern, scheme);
@@ -225,7 +225,7 @@
      *                      additional control over which pattern matches are
      *                      to be converted into links.
      */
-    public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
+    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
         if (Build.VERSION.SDK_INT >= 26) {
@@ -252,7 +252,7 @@
      *                      over which pattern matches are to be converted into links.
      *  @param transformFilter Filter to allow the client code to update the link found.
      */
-    public static final void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
+    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
             @Nullable String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
         if (Build.VERSION.SDK_INT >= 26) {
@@ -278,7 +278,7 @@
      *  @param scheme       URL scheme string (eg <code>http://</code>) to be
      *                      prepended to the links that do not start with this scheme.
      */
-    public static final boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
+    public static boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
             @Nullable String scheme) {
         if (Build.VERSION.SDK_INT >= 26) {
             return Linkify.addLinks(text, pattern, scheme);
@@ -301,7 +301,7 @@
      *
      * @return True if at least one link is found and applied.
      */
-    public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
+    public static boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable String scheme, @Nullable MatchFilter matchFilter,
             @Nullable TransformFilter transformFilter) {
         if (Build.VERSION.SDK_INT >= 26) {
@@ -327,7 +327,7 @@
      *
      * @return True if at least one link is found and applied.
      */
-    public static final boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
+    public static boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
             @Nullable  String defaultScheme, @Nullable String[] schemes,
             @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
         if (Build.VERSION.SDK_INT >= 26) {
@@ -436,7 +436,7 @@
         text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
     }
 
-    private static final void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
+    private static void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
         String string = s.toString();
         String address;
         int base = 0;
@@ -477,7 +477,7 @@
         }
     }
 
-    private static final void pruneOverlaps(ArrayList<LinkSpec> links, Spannable text) {
+    private static void pruneOverlaps(ArrayList<LinkSpec> links, Spannable text) {
         // Append spans added by framework
         URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class);
         for (int i = 0; i < urlSpans.length; i++) {
diff --git a/compat/src/main/java/android/support/v4/util/MapCollections.java b/compat/src/main/java/android/support/v4/util/MapCollections.java
index 1a0ab6b..f20b098 100644
--- a/compat/src/main/java/android/support/v4/util/MapCollections.java
+++ b/compat/src/main/java/android/support/v4/util/MapCollections.java
@@ -131,7 +131,7 @@
         }
 
         @Override
-        public final boolean equals(Object o) {
+        public boolean equals(Object o) {
             if (!mEntryValid) {
                 throw new IllegalStateException(
                         "This container does not support retaining Map.Entry objects");
@@ -145,7 +145,7 @@
         }
 
         @Override
-        public final int hashCode() {
+        public int hashCode() {
             if (!mEntryValid) {
                 throw new IllegalStateException(
                         "This container does not support retaining Map.Entry objects");
@@ -157,7 +157,7 @@
         }
 
         @Override
-        public final String toString() {
+        public String toString() {
             return getKey() + "=" + getValue();
         }
     }
diff --git a/compat/src/main/java/android/support/v4/util/Pools.java b/compat/src/main/java/android/support/v4/util/Pools.java
index 99fd888..678663a 100644
--- a/compat/src/main/java/android/support/v4/util/Pools.java
+++ b/compat/src/main/java/android/support/v4/util/Pools.java
@@ -51,13 +51,13 @@
      *
      * @param <T> The pooled type.
      */
-    public static interface Pool<T> {
+    public interface Pool<T> {
 
         /**
          * @return An instance from the pool if such, null otherwise.
          */
         @Nullable
-        public T acquire();
+        T acquire();
 
         /**
          * Release an instance to the pool.
@@ -67,7 +67,7 @@
          *
          * @throws IllegalStateException If the instance is already in the pool.
          */
-        public boolean release(@NonNull T instance);
+        boolean release(@NonNull T instance);
     }
 
     private Pools() {
diff --git a/compat/src/main/java/android/support/v4/view/ActionProvider.java b/compat/src/main/java/android/support/v4/view/ActionProvider.java
index e6ffd98..fd82740 100644
--- a/compat/src/main/java/android/support/v4/view/ActionProvider.java
+++ b/compat/src/main/java/android/support/v4/view/ActionProvider.java
@@ -318,7 +318,7 @@
     @RestrictTo(LIBRARY_GROUP)
     public interface SubUiVisibilityListener {
 
-        public void onSubUiVisibilityChanged(boolean isVisible);
+        void onSubUiVisibilityChanged(boolean isVisible);
     }
 
     /**
@@ -328,6 +328,6 @@
      * @see ActionProvider#isVisible()
      */
     public interface VisibilityListener {
-        public void onActionProviderVisibilityChanged(boolean isVisible);
+        void onActionProviderVisibilityChanged(boolean isVisible);
     }
 }
diff --git a/compat/src/main/java/android/support/v4/view/GestureDetectorCompat.java b/compat/src/main/java/android/support/v4/view/GestureDetectorCompat.java
index fbecce9..4bf2446 100644
--- a/compat/src/main/java/android/support/v4/view/GestureDetectorCompat.java
+++ b/compat/src/main/java/android/support/v4/view/GestureDetectorCompat.java
@@ -148,7 +148,7 @@
          *
          * @throws NullPointerException if {@code listener} is null.
          */
-        public GestureDetectorCompatImplBase(Context context, OnGestureListener listener,
+        GestureDetectorCompatImplBase(Context context, OnGestureListener listener,
                 Handler handler) {
             if (handler != null) {
                 mHandler = new GestureHandler(handler);
@@ -465,7 +465,7 @@
     static class GestureDetectorCompatImplJellybeanMr2 implements GestureDetectorCompatImpl {
         private final GestureDetector mDetector;
 
-        public GestureDetectorCompatImplJellybeanMr2(Context context, OnGestureListener listener,
+        GestureDetectorCompatImplJellybeanMr2(Context context, OnGestureListener listener,
                 Handler handler) {
             mDetector = new GestureDetector(context, listener, handler);
         }
diff --git a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
index fe26b6e..0822d01 100644
--- a/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
+++ b/compat/tests/java/android/support/v4/text/util/LinkifyCompatTest.java
@@ -50,7 +50,7 @@
 
     private MatchFilter mMatchFilterStartWithDot = new MatchFilter() {
         @Override
-        public final boolean acceptMatch(final CharSequence s, final int start, final int end) {
+        public boolean acceptMatch(final CharSequence s, final int start, final int end) {
             if (start == 0) {
                 return true;
             }
@@ -65,7 +65,7 @@
 
     private TransformFilter mTransformFilterUpperChar = new TransformFilter() {
         @Override
-        public final String transformUrl(final Matcher match, String url) {
+        public String transformUrl(final Matcher match, String url) {
             StringBuilder buffer = new StringBuilder();
             String matchingRegion = match.group();
 
diff --git a/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java b/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
index cf2b52f..42cf80e 100644
--- a/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
+++ b/compat/tests/java/android/support/v4/widget/TextViewCompatTest.java
@@ -78,7 +78,7 @@
         private int mWidth;
         private int mHeight;
 
-        public TestDrawable(@ColorInt int color, int width, int height) {
+        TestDrawable(@ColorInt int color, int width, int height) {
             super(color);
             mWidth = width;
             mHeight = height;
diff --git a/core-ui/api/current.txt b/core-ui/api/current.txt
index 77e805d..cdc1830 100644
--- a/core-ui/api/current.txt
+++ b/core-ui/api/current.txt
@@ -379,10 +379,10 @@
   public class ContentLoadingProgressBar extends android.widget.ProgressBar {
     ctor public ContentLoadingProgressBar(android.content.Context);
     ctor public ContentLoadingProgressBar(android.content.Context, android.util.AttributeSet);
-    method public void hide();
+    method public synchronized void hide();
     method public void onAttachedToWindow();
     method public void onDetachedFromWindow();
-    method public void show();
+    method public synchronized void show();
   }
 
   public abstract class CursorAdapter extends android.widget.BaseAdapter implements android.widget.Filterable {
diff --git a/core-ui/src/main/java/android/support/v4/view/animation/LookupTableInterpolator.java b/core-ui/src/main/java/android/support/v4/view/animation/LookupTableInterpolator.java
index c234177..98769e6 100644
--- a/core-ui/src/main/java/android/support/v4/view/animation/LookupTableInterpolator.java
+++ b/core-ui/src/main/java/android/support/v4/view/animation/LookupTableInterpolator.java
@@ -27,7 +27,7 @@
     private final float[] mValues;
     private final float mStepSize;
 
-    public LookupTableInterpolator(float[] values) {
+    protected LookupTableInterpolator(float[] values) {
         mValues = values;
         mStepSize = 1f / (mValues.length - 1);
     }
diff --git a/core-ui/src/main/java/android/support/v4/widget/SwipeRefreshLayout.java b/core-ui/src/main/java/android/support/v4/widget/SwipeRefreshLayout.java
index ca04e46..5e10f51 100644
--- a/core-ui/src/main/java/android/support/v4/widget/SwipeRefreshLayout.java
+++ b/core-ui/src/main/java/android/support/v4/widget/SwipeRefreshLayout.java
@@ -418,12 +418,7 @@
 
     private void startScaleUpAnimation(AnimationListener listener) {
         mCircleView.setVisibility(View.VISIBLE);
-        if (android.os.Build.VERSION.SDK_INT >= 11) {
-            // Pre API 11, alpha is used in place of scale up to show the
-            // progress circle appearing.
-            // Don't adjust the alpha during appearance otherwise.
-            mProgress.setAlpha(MAX_ALPHA);
-        }
+        mProgress.setAlpha(MAX_ALPHA);
         mScaleAnimation = new Animation() {
             @Override
             public void applyTransformation(float interpolatedTime, Transformation t) {
diff --git a/core-ui/tests/java/android/support/v4/view/ViewPagerActions.java b/core-ui/tests/java/android/support/v4/view/ViewPagerActions.java
index 7e175dd..4c42d72 100644
--- a/core-ui/tests/java/android/support/v4/view/ViewPagerActions.java
+++ b/core-ui/tests/java/android/support/v4/view/ViewPagerActions.java
@@ -109,7 +109,7 @@
             }
 
             @Override
-            public final void perform(UiController uiController, View view) {
+            public void perform(UiController uiController, View view) {
                 final ViewPager viewPager = (ViewPager) view;
                 // Add a custom tracker listener
                 final CustomViewPagerListener customListener = new CustomViewPagerListener();
diff --git a/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java b/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
index 83c1dc1..d78b49b 100644
--- a/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
+++ b/core-ui/tests/java/android/support/v4/widget/ExploreByTouchHelperTest.java
@@ -115,7 +115,7 @@
     private static class ParentBoundsHelper extends ExploreByTouchHelper {
         private final View mHost;
 
-        public ParentBoundsHelper(View host) {
+        ParentBoundsHelper(View host) {
             super(host);
 
             mHost = host;
diff --git a/core-utils/java/android/support/v4/content/FileProvider.java b/core-utils/java/android/support/v4/content/FileProvider.java
index 16164be..ca22407 100644
--- a/core-utils/java/android/support/v4/content/FileProvider.java
+++ b/core-utils/java/android/support/v4/content/FileProvider.java
@@ -669,12 +669,12 @@
         /**
          * Return a {@link Uri} that represents the given {@link File}.
          */
-        public Uri getUriForFile(File file);
+        Uri getUriForFile(File file);
 
         /**
          * Return a {@link File} that represents the given {@link Uri}.
          */
-        public File getFileForUri(Uri uri);
+        File getFileForUri(Uri uri);
     }
 
     /**
@@ -691,7 +691,7 @@
         private final String mAuthority;
         private final HashMap<String, File> mRoots = new HashMap<String, File>();
 
-        public SimplePathStrategy(String authority) {
+        SimplePathStrategy(String authority) {
             mAuthority = authority;
         }
 
@@ -699,7 +699,7 @@
          * Add a mapping from a name to a filesystem root. The provider only offers
          * access to files that live under configured roots.
          */
-        public void addRoot(String name, File root) {
+        void addRoot(String name, File root) {
             if (TextUtils.isEmpty(name)) {
                 throw new IllegalArgumentException("Name must not be empty");
             }
diff --git a/development/checkstyle/config/support-lib.xml b/development/checkstyle/config/support-lib.xml
index 43bbb82..e664be3 100644
--- a/development/checkstyle/config/support-lib.xml
+++ b/development/checkstyle/config/support-lib.xml
@@ -28,6 +28,8 @@
         &defaultJavadocChecks;
         &defaultTreewalkerChecks;
 
+        <module name="RedundantModifierCheck" />
+
         <!-- Custom support library check for @RestrictTo / @hide. -->
         <module name="com.android.support.checkstyle.MismatchedAnnotationCheck">
             <property name="severity" value="error" />
diff --git a/fragment/api/current.txt b/fragment/api/current.txt
index c3fc2ea..e052a0b 100644
--- a/fragment/api/current.txt
+++ b/fragment/api/current.txt
@@ -178,7 +178,7 @@
 
   public class FragmentController {
     method public void attachHost(android.support.v4.app.Fragment);
-    method public static final android.support.v4.app.FragmentController createController(android.support.v4.app.FragmentHostCallback<?>);
+    method public static android.support.v4.app.FragmentController createController(android.support.v4.app.FragmentHostCallback<?>);
     method public void dispatchActivityCreated();
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
     method public boolean dispatchContextItemSelected(android.view.MenuItem);
diff --git a/fragment/src/androidTest/java/android/support/v4/app/FragmentAnimationTest.java b/fragment/src/androidTest/java/android/support/v4/app/FragmentAnimationTest.java
index 8c3d6ed..4966316 100644
--- a/fragment/src/androidTest/java/android/support/v4/app/FragmentAnimationTest.java
+++ b/fragment/src/androidTest/java/android/support/v4/app/FragmentAnimationTest.java
@@ -22,6 +22,7 @@
 import static org.junit.Assert.assertTrue;
 
 import android.app.Instrumentation;
+import android.os.Bundle;
 import android.os.Parcelable;
 import android.support.annotation.AnimRes;
 import android.support.fragment.test.R;
@@ -32,8 +33,11 @@
 import android.support.v4.app.test.FragmentTestActivity;
 import android.support.v4.view.ViewCompat;
 import android.util.Pair;
+import android.view.LayoutInflater;
 import android.view.View;
+import android.view.ViewGroup;
 import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
 import android.view.animation.TranslateAnimation;
 
 import org.junit.Before;
@@ -41,6 +45,9 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @MediumTest
 @RunWith(AndroidJUnit4.class)
 public class FragmentAnimationTest {
@@ -442,6 +449,54 @@
         });
     }
 
+    // When a view is animated out, is parent should be null after the animation completes
+    @Test
+    public void parentNullAfterAnimation() throws Throwable {
+        final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager();
+
+        final EndAnimationListenerFragment fragment1 = new EndAnimationListenerFragment();
+        fm.beginTransaction()
+                .add(R.id.fragmentContainer, fragment1)
+                .commit();
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        final EndAnimationListenerFragment fragment2 = new EndAnimationListenerFragment();
+
+        fm.beginTransaction()
+                .setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out,
+                        android.R.anim.fade_in, android.R.anim.fade_out)
+                .replace(R.id.fragmentContainer, fragment2)
+                .addToBackStack(null)
+                .commit();
+
+        FragmentTestUtil.waitForExecution(mActivityRule);
+
+        assertTrue(fragment1.exitLatch.await(1, TimeUnit.SECONDS));
+        assertTrue(fragment2.enterLatch.await(1, TimeUnit.SECONDS));
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertNotNull(fragment1.view);
+                assertNotNull(fragment2.view);
+                assertNull(fragment1.view.getParent());
+            }
+        });
+
+        // Now pop the transaction
+        FragmentTestUtil.popBackStackImmediate(mActivityRule);
+
+        assertTrue(fragment2.exitLatch.await(1, TimeUnit.SECONDS));
+        assertTrue(fragment1.enterLatch.await(1, TimeUnit.SECONDS));
+
+        mActivityRule.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                assertNull(fragment2.view.getParent());
+            }
+        });
+    }
+
     private void assertEnterPopExit(AnimatorFragment fragment) throws Throwable {
         assertFragmentAnimation(fragment, 1, true, ENTER);
 
@@ -516,4 +571,50 @@
             return this.animation;
         }
     }
+
+    public static class EndAnimationListenerFragment extends StrictViewFragment {
+        public View view;
+        public final CountDownLatch enterLatch = new CountDownLatch(1);
+        public final CountDownLatch exitLatch = new CountDownLatch(1);
+
+        @Override
+        public View onCreateView(LayoutInflater inflater, ViewGroup container,
+                Bundle savedInstanceState) {
+            if (view != null) {
+                return view;
+            }
+            view = super.onCreateView(inflater, container, savedInstanceState);
+            return view;
+        }
+
+        @Override
+        public Animation onCreateAnimation(int transit, final boolean enter, int nextAnim) {
+            if (nextAnim == 0) {
+                return null;
+            }
+            Animation anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);
+            if (anim != null) {
+                anim.setAnimationListener(new Animation.AnimationListener() {
+                    @Override
+                    public void onAnimationStart(Animation animation) {
+                    }
+
+                    @Override
+                    public void onAnimationEnd(Animation animation) {
+                        if (enter) {
+                            enterLatch.countDown();
+                        } else {
+                            exitLatch.countDown();
+                        }
+                    }
+
+                    @Override
+                    public void onAnimationRepeat(Animation animation) {
+
+                    }
+                });
+            }
+            return anim;
+        }
+    }
 }
diff --git a/fragment/src/main/java/android/support/v4/app/FragmentController.java b/fragment/src/main/java/android/support/v4/app/FragmentController.java
index 48b3602..2dfb6b0 100644
--- a/fragment/src/main/java/android/support/v4/app/FragmentController.java
+++ b/fragment/src/main/java/android/support/v4/app/FragmentController.java
@@ -43,7 +43,7 @@
     /**
      * Returns a {@link FragmentController}.
      */
-    public static final FragmentController createController(FragmentHostCallback<?> callbacks) {
+    public static FragmentController createController(FragmentHostCallback<?> callbacks) {
         return new FragmentController(callbacks);
     }
 
diff --git a/fragment/src/main/java/android/support/v4/app/FragmentManager.java b/fragment/src/main/java/android/support/v4/app/FragmentManager.java
index 16103f8..56040b2 100644
--- a/fragment/src/main/java/android/support/v4/app/FragmentManager.java
+++ b/fragment/src/main/java/android/support/v4/app/FragmentManager.java
@@ -61,6 +61,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.animation.ScaleAnimation;
+import android.view.animation.Transformation;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -1504,8 +1505,8 @@
                         dispatchOnFragmentViewDestroyed(f, false);
                         if (f.mView != null && f.mContainer != null) {
                             // Stop any current animations:
-                            f.mView.clearAnimation();
                             f.mContainer.endViewTransition(f.mView);
+                            f.mView.clearAnimation();
                             AnimationOrAnimator anim = null;
                             if (mCurState > Fragment.INITIALIZING && !mDestroyed
                                     && f.mView.getVisibility() == View.VISIBLE
@@ -1598,21 +1599,21 @@
         container.startViewTransition(viewToAnimate);
         fragment.setStateAfterAnimating(newState);
         if (anim.animation != null) {
-            Animation animation = anim.animation;
+            Animation animation =
+                    new EndViewTransitionAnimator(anim.animation, container, viewToAnimate);
             fragment.setAnimatingAway(fragment.mView);
             AnimationListener listener = getAnimationListener(animation);
             animation.setAnimationListener(new AnimationListenerWrapper(listener) {
                 @Override
                 public void onAnimationEnd(Animation animation) {
                     super.onAnimationEnd(animation);
+
                     // onAnimationEnd() comes during draw(), so there can still be some
                     // draw events happening after this call. We don't want to detach
                     // the view until after the onAnimationEnd()
                     container.post(new Runnable() {
                         @Override
                         public void run() {
-                            container.endViewTransition(viewToAnimate);
-
                             if (fragment.getAnimatingAway() != null) {
                                 fragment.setAnimatingAway(null);
                                 moveToState(fragment, fragment.getStateAfterAnimating(), 0, 0,
@@ -4003,4 +4004,58 @@
             animation.removeListener(this);
         }
     }
+
+    /**
+     * We must call endViewTransition() before the animation ends or else the parent doesn't
+     * get nulled out. We use both startViewTransition() and startAnimation() to solve a problem
+     * with Views remaining in the hierarchy as disappearing children after the view has been
+     * removed in some edge cases.
+     */
+    private static class EndViewTransitionAnimator extends AnimationSet implements Runnable {
+        private final ViewGroup mParent;
+        private final View mChild;
+        private boolean mEnded;
+        private boolean mTransitionEnded;
+
+        EndViewTransitionAnimator(@NonNull Animation animation,
+                @NonNull ViewGroup parent, @NonNull View child) {
+            super(false);
+            mParent = parent;
+            mChild = child;
+            addAnimation(animation);
+        }
+
+        @Override
+        public boolean getTransformation(long currentTime, Transformation t) {
+            if (mEnded) {
+                return !mTransitionEnded;
+            }
+            boolean more = super.getTransformation(currentTime, t);
+            if (!more) {
+                mEnded = true;
+                mParent.post(this);
+            }
+            return true;
+        }
+
+        @Override
+        public boolean getTransformation(long currentTime, Transformation outTransformation,
+                float scale) {
+            if (mEnded) {
+                return !mTransitionEnded;
+            }
+            boolean more = super.getTransformation(currentTime, outTransformation, scale);
+            if (!more) {
+                mEnded = true;
+                mParent.post(this);
+            }
+            return true;
+        }
+
+        @Override
+        public void run() {
+            mParent.endViewTransition(mChild);
+            mTransitionEnded = true;
+        }
+    }
 }
diff --git a/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java b/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
index 943f1aa..a47cf59 100644
--- a/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
+++ b/graphics/drawable/static/src/main/java/android/support/graphics/drawable/VectorDrawableCompat.java
@@ -651,11 +651,7 @@
             case 15:
                 return Mode.SCREEN;
             case 16:
-                if (Build.VERSION.SDK_INT >= 11) {
-                    return Mode.ADD;
-                } else {
-                    return defaultMode;
-                }
+                return Mode.ADD;
             default:
                 return defaultMode;
         }
diff --git a/leanback/api/current.txt b/leanback/api/current.txt
index b045d8f..56a8f1a 100644
--- a/leanback/api/current.txt
+++ b/leanback/api/current.txt
@@ -1895,7 +1895,7 @@
     ctor public GuidedActionDiffCallback();
     method public boolean areContentsTheSame(android.support.v17.leanback.widget.GuidedAction, android.support.v17.leanback.widget.GuidedAction);
     method public boolean areItemsTheSame(android.support.v17.leanback.widget.GuidedAction, android.support.v17.leanback.widget.GuidedAction);
-    method public static final android.support.v17.leanback.widget.GuidedActionDiffCallback getInstance();
+    method public static android.support.v17.leanback.widget.GuidedActionDiffCallback getInstance();
   }
 
   public class GuidedActionEditText extends android.widget.EditText implements android.support.v17.leanback.widget.ImeKeyMonitor {
diff --git a/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java b/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
index 9be350d..1c52946 100644
--- a/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
+++ b/leanback/src/android/support/v17/leanback/app/GuidedStepFragment.java
@@ -1199,7 +1199,7 @@
         return EXTRA_BUTTON_ACTION_PREFIX + action.getId();
     }
 
-    final static boolean isSaveEnabled(GuidedAction action) {
+    static boolean isSaveEnabled(GuidedAction action) {
         return action.isAutoSaveRestoreEnabled() && action.getId() != GuidedAction.NO_ID;
     }
 
diff --git a/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java b/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
index e276d07..ce48b3c 100644
--- a/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
+++ b/leanback/src/android/support/v17/leanback/app/GuidedStepSupportFragment.java
@@ -1194,7 +1194,7 @@
         return EXTRA_BUTTON_ACTION_PREFIX + action.getId();
     }
 
-    final static boolean isSaveEnabled(GuidedAction action) {
+    static boolean isSaveEnabled(GuidedAction action) {
         return action.isAutoSaveRestoreEnabled() && action.getId() != GuidedAction.NO_ID;
     }
 
diff --git a/leanback/src/android/support/v17/leanback/widget/GuidedAction.java b/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
index 8898d48..a0c198d 100644
--- a/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
+++ b/leanback/src/android/support/v17/leanback/widget/GuidedAction.java
@@ -938,7 +938,7 @@
         }
     }
 
-    final static boolean isPasswordVariant(int inputType) {
+    static boolean isPasswordVariant(int inputType) {
         final int variation = inputType & InputType.TYPE_MASK_VARIATION;
         return variation == InputType.TYPE_TEXT_VARIATION_PASSWORD
                 || variation == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
diff --git a/leanback/src/android/support/v17/leanback/widget/GuidedActionDiffCallback.java b/leanback/src/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
index d4d4d77..0894720 100644
--- a/leanback/src/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
+++ b/leanback/src/android/support/v17/leanback/widget/GuidedActionDiffCallback.java
@@ -30,7 +30,7 @@
      * Returns the singleton GuidedActionDiffCallback.
      * @return The singleton GuidedActionDiffCallback.
      */
-    public static final GuidedActionDiffCallback getInstance() {
+    public static GuidedActionDiffCallback getInstance() {
         return sInstance;
     }
 
diff --git a/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java b/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
index 75769f5..4d4a1eb 100644
--- a/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
+++ b/leanback/tests/java/android/support/v17/leanback/app/BrowseFragmentTest.java
@@ -43,7 +43,7 @@
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
 import android.app.Fragment;
 import android.support.v7.widget.RecyclerView;
 import android.view.KeyEvent;
@@ -115,6 +115,7 @@
     public void testTouchMode() throws Throwable {
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
+        intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY , 0L);
         mActivity = activityTestRule.launchActivity(intent);
 
         waitForEntranceTransitionFinished();
@@ -134,7 +135,7 @@
 
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
@@ -150,7 +151,7 @@
 
     @Test
     public void testTwoBackKeysWithoutBackStack() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
@@ -207,13 +208,18 @@
     @Test
     public void testPressCenterBeforeMainFragmentCreated() throws Throwable {
         Intent intent = new Intent();
-        intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 0);
+        intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 0L);
         mActivity = activityTestRule.launchActivity(intent);
 
         final BrowseFragment fragment = mActivity.getBrowseTestFragment();
         fragment.getMainFragmentRegistry().registerFragment(MyRow.class, new MyFragmentFactory());
 
-        final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new RowHeaderPresenter());
+        final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new RowPresenter() {
+            protected ViewHolder createRowViewHolder(ViewGroup parent) {
+                View view = new FrameLayout(parent.getContext());
+                return new RowPresenter.ViewHolder(view);
+            }
+        });
         adapter.add(new MyRow());
         activityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -238,7 +244,7 @@
         final int selectRow = 10;
         final int selectItem = 20;
         Intent intent = new Intent();
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
@@ -275,7 +281,7 @@
 
     @Test
     public void activityRecreate_notCrash() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
@@ -296,7 +302,7 @@
 
     @Test
     public void lateLoadingHeaderDisabled() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_HEADERS_STATE,
diff --git a/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java b/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
index de94551..2011b8e 100644
--- a/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
+++ b/leanback/tests/java/android/support/v17/leanback/app/BrowseSupportFragmentTest.java
@@ -40,7 +40,7 @@
 import android.support.v17.leanback.widget.ListRowPresenter;
 import android.support.v17.leanback.widget.Presenter;
 import android.support.v17.leanback.widget.Row;
-import android.support.v17.leanback.widget.RowHeaderPresenter;
+import android.support.v17.leanback.widget.RowPresenter;
 import android.support.v4.app.Fragment;
 import android.support.v7.widget.RecyclerView;
 import android.view.KeyEvent;
@@ -112,6 +112,7 @@
     public void testTouchMode() throws Throwable {
         Intent intent = new Intent();
         intent.putExtra(BrowseFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
+        intent.putExtra(BrowseFragmentTestActivity.EXTRA_LOAD_DATA_DELAY , 0L);
         mActivity = activityTestRule.launchActivity(intent);
 
         waitForEntranceTransitionFinished();
@@ -131,7 +132,7 @@
 
     @Test
     public void testTwoBackKeysWithBackStack() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
@@ -147,7 +148,7 @@
 
     @Test
     public void testTwoBackKeysWithoutBackStack() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
@@ -204,13 +205,18 @@
     @Test
     public void testPressCenterBeforeMainFragmentCreated() throws Throwable {
         Intent intent = new Intent();
-        intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 0);
+        intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, 0L);
         mActivity = activityTestRule.launchActivity(intent);
 
         final BrowseSupportFragment fragment = mActivity.getBrowseTestSupportFragment();
         fragment.getMainFragmentRegistry().registerFragment(MyRow.class, new MyFragmentFactory());
 
-        final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new RowHeaderPresenter());
+        final ArrayObjectAdapter adapter = new ArrayObjectAdapter(new RowPresenter() {
+            protected ViewHolder createRowViewHolder(ViewGroup parent) {
+                View view = new FrameLayout(parent.getContext());
+                return new RowPresenter.ViewHolder(view);
+            }
+        });
         adapter.add(new MyRow());
         activityTestRule.runOnUiThread(new Runnable() {
             @Override
@@ -235,7 +241,7 @@
         final int selectRow = 10;
         final int selectItem = 20;
         Intent intent = new Intent();
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , true);
         mActivity = activityTestRule.launchActivity(intent);
@@ -272,7 +278,7 @@
 
     @Test
     public void activityRecreate_notCrash() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_ADD_TO_BACKSTACK , false);
@@ -293,7 +299,7 @@
 
     @Test
     public void lateLoadingHeaderDisabled() throws Throwable {
-        final long dataLoadingDelay = 1000;
+        final long dataLoadingDelay = 0L;
         Intent intent = new Intent();
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_LOAD_DATA_DELAY, dataLoadingDelay);
         intent.putExtra(BrowseSupportFragmentTestActivity.EXTRA_HEADERS_STATE,
diff --git a/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java b/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
index 6a34c53..74fe87a 100644
--- a/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
+++ b/leanback/tests/java/android/support/v17/leanback/app/BrowseTestFragment.java
@@ -95,9 +95,7 @@
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
-                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
-                        + " " + rowViewHolder
-                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
+                Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
         if (TEST_ENTRANCE_TRANSITION) {
diff --git a/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java b/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
index 373d7a3..0986f0d 100644
--- a/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
+++ b/leanback/tests/java/android/support/v17/leanback/app/BrowseTestSupportFragment.java
@@ -92,9 +92,7 @@
             @Override
             public void onItemSelected(Presenter.ViewHolder itemViewHolder, Object item,
                     RowPresenter.ViewHolder rowViewHolder, Row row) {
-                Log.i(TAG, "onItemSelected: " + item + " row " + row.getHeaderItem().getName()
-                        + " " + rowViewHolder
-                        + " " + ((ListRowPresenter.ViewHolder) rowViewHolder).getGridView());
+                Log.i(TAG, "onItemSelected: " + item + " row " + row);
             }
         });
         if (TEST_ENTRANCE_TRANSITION) {
diff --git a/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java b/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
index 68f04e8..23aed28 100644
--- a/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
+++ b/lifecycle/common/src/test/java/android/arch/lifecycle/ReflectiveGenericLifecycleObserverTest.java
@@ -30,6 +30,7 @@
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.core.Is.is;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
@@ -185,6 +186,7 @@
         ReflectiveGenericLifecycleObserver observer = new ReflectiveGenericLifecycleObserver(obj);
         try {
             observer.onStateChanged(mOwner, ON_START);
+            fail();
         } catch (Exception e) {
             assertThat("exception cause is wrong",
                     e.getCause() instanceof UnprecedentedError);
@@ -247,13 +249,14 @@
         new ReflectiveGenericLifecycleObserver(observer);
     }
 
-    class BaseClass1 implements LifecycleObserver {
+    static class BaseClass1 implements LifecycleObserver {
         @OnLifecycleEvent(ON_START)
         void foo(LifecycleOwner owner) {
         }
     }
 
-    class DerivedClass1 extends BaseClass1 {
+    static class DerivedClass1 extends BaseClass1 {
+        @Override
         @OnLifecycleEvent(ON_STOP)
         void foo(LifecycleOwner owner) {
         }
@@ -264,13 +267,13 @@
         new ReflectiveGenericLifecycleObserver(new DerivedClass1());
     }
 
-    class BaseClass2 implements LifecycleObserver {
+    static class BaseClass2 implements LifecycleObserver {
         @OnLifecycleEvent(ON_START)
         void foo(LifecycleOwner owner) {
         }
     }
 
-    class DerivedClass2 extends BaseClass1 {
+    static class DerivedClass2 extends BaseClass1 {
         @OnLifecycleEvent(ON_STOP)
         void foo() {
         }
@@ -289,7 +292,7 @@
         verify(obj, never()).foo(Matchers.<LifecycleOwner>any());
     }
 
-    class BaseClass3 implements LifecycleObserver {
+    static class BaseClass3 implements LifecycleObserver {
         @OnLifecycleEvent(ON_START)
         void foo(LifecycleOwner owner) {
         }
@@ -300,7 +303,7 @@
         void foo(LifecycleOwner owner);
     }
 
-    class DerivedClass3 extends BaseClass3 implements Interface3 {
+    static class DerivedClass3 extends BaseClass3 implements Interface3 {
         @Override
         public void foo(LifecycleOwner owner) {
         }
@@ -311,7 +314,7 @@
         new ReflectiveGenericLifecycleObserver(new DerivedClass3());
     }
 
-    class BaseClass4 implements LifecycleObserver {
+    static class BaseClass4 implements LifecycleObserver {
         @OnLifecycleEvent(ON_START)
         void foo(LifecycleOwner owner) {
         }
@@ -322,7 +325,7 @@
         void foo(LifecycleOwner owner);
     }
 
-    class DerivedClass4 extends BaseClass4 implements Interface4 {
+    static class DerivedClass4 extends BaseClass4 implements Interface4 {
         @Override
         @OnLifecycleEvent(ON_START)
         public void foo(LifecycleOwner owner) {
@@ -352,7 +355,7 @@
         void foo(LifecycleOwner owner);
     }
 
-    class DerivedClass5 implements InterfaceStart, InterfaceStop {
+    static class DerivedClass5 implements InterfaceStart, InterfaceStop {
         @Override
         public void foo(LifecycleOwner owner) {
         }
diff --git a/media-compat/version-compat-tests/current/client/tests/src/android/support/mediacompat/client/AudioAttributesCompatTest.java b/media-compat/version-compat-tests/current/client/tests/src/android/support/mediacompat/client/AudioAttributesCompatTest.java
index 675c0fc..ca44a9a 100644
--- a/media-compat/version-compat-tests/current/client/tests/src/android/support/mediacompat/client/AudioAttributesCompatTest.java
+++ b/media-compat/version-compat-tests/current/client/tests/src/android/support/mediacompat/client/AudioAttributesCompatTest.java
@@ -37,13 +37,13 @@
 @RunWith(AndroidJUnit4.class)
 public class AudioAttributesCompatTest {
     // some macros for conciseness
-    static final AudioAttributesCompat.Builder mkBuilder(
+    static AudioAttributesCompat.Builder mkBuilder(
             @AudioAttributesCompat.AttributeContentType int type,
             @AudioAttributesCompat.AttributeUsage int usage) {
         return new AudioAttributesCompat.Builder().setContentType(type).setUsage(usage);
     }
 
-    static final AudioAttributesCompat.Builder mkBuilder(int legacyStream) {
+    static AudioAttributesCompat.Builder mkBuilder(int legacyStream) {
         return new AudioAttributesCompat.Builder().setLegacyStreamType(legacyStream);
     }
 
diff --git a/recyclerview-selection/src/main/java/androidx/widget/recyclerview/selection/FocusDelegate.java b/recyclerview-selection/src/main/java/androidx/widget/recyclerview/selection/FocusDelegate.java
index 1ecfcc4..c1b5fa0 100644
--- a/recyclerview-selection/src/main/java/androidx/widget/recyclerview/selection/FocusDelegate.java
+++ b/recyclerview-selection/src/main/java/androidx/widget/recyclerview/selection/FocusDelegate.java
@@ -29,7 +29,7 @@
  */
 public abstract class FocusDelegate<K> {
 
-    static final <K> FocusDelegate<K> dummy() {
+    static <K> FocusDelegate<K> dummy() {
         return new FocusDelegate<K>() {
             @Override
             public void focusItem(@NonNull ItemDetails<K> item) {
diff --git a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/LocalPlayer.java b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/LocalPlayer.java
index 03f3318..133f4ea 100644
--- a/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/LocalPlayer.java
+++ b/samples/Support7Demos/src/main/java/com/example/android/supportv7/media/LocalPlayer.java
@@ -375,7 +375,7 @@
     }
 
     private static final class ICSMediaPlayer {
-        public static final void setSurface(MediaPlayer player, Surface surface) {
+        public static void setSurface(MediaPlayer player, Surface surface) {
             player.setSurface(surface);
         }
     }
diff --git a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
index 198f092..6975d61 100644
--- a/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
+++ b/samples/SupportCarDemos/src/main/java/com/example/androidx/car/SeekbarListItemActivity.java
@@ -18,7 +18,6 @@
 
 import android.app.Activity;
 import android.content.Context;
-import android.graphics.Color;
 import android.os.Bundle;
 import android.widget.SeekBar;
 import android.widget.Toast;
@@ -40,17 +39,12 @@
 
     PagedListView mPagedListView;
 
-    private static int pixelToDip(Context context, int pixels) {
-        return (int) (pixels / context.getResources().getDisplayMetrics().density);
-    }
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_paged_list_view);
 
         mPagedListView = findViewById(R.id.paged_list_view);
-        mPagedListView.setBackgroundColor(Color.BLUE);
 
         ListItemAdapter adapter = new ListItemAdapter(this,
                 new SampleProvider(this), ListItemAdapter.BackgroundStyle.PANEL);
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
index 23da45e..a265ce2 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SampleSliceProvider.java
@@ -25,6 +25,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
+import android.os.Handler;
 import android.provider.Settings;
 import android.support.annotation.NonNull;
 import android.text.SpannableString;
@@ -48,8 +49,11 @@
             "com.example.androidx.slice.action.TOAST";
     public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE";
 
+    public static final int LOADING_DELAY_MS = 4000;
+
     public static final String[] URI_PATHS = {"message", "wifi", "note", "ride", "toggle",
-            "toggle2", "contact", "gallery", "weather", "reservation"};
+            "toggle2", "contact", "gallery", "weather", "reservation", "loadlist", "loadlist2",
+            "loadgrid", "loadgrid2"};
 
     /**
      * @return Uri with the provided path
@@ -97,6 +101,14 @@
                 return createWeather(sliceUri);
             case "/reservation":
                 return createReservationSlice(sliceUri);
+            case "/loadlist":
+                return createLoadingSlice(sliceUri, false /* loadAll */, true /* isList */);
+            case "/loadlist2":
+                return createLoadingSlice(sliceUri, true /* loadAll */, true /* isList */);
+            case "/loadgrid":
+                return createLoadingSlice(sliceUri, false /* loadAll */, false /* isList */);
+            case "/loadgrid2":
+                return createLoadingSlice(sliceUri, true /* loadAll */, false /* isList */);
         }
         throw new IllegalArgumentException("Unknown uri " + sliceUri);
     }
@@ -246,15 +258,13 @@
         return new ListBuilder(getContext(), sliceUri)
                 .setColor(0xff0F9D58)
                 .addRow(b -> b
-                    .setContentIntent(getBroadcastIntent(ACTION_TOAST, "work"))
                     .setTitle("Work")
                     .setSubtitle(workSubtitle)
                     .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_work),
                             getBroadcastIntent(ACTION_TOAST, "work")))
                 .addRow(b -> b
-                    .setContentIntent(getBroadcastIntent(ACTION_TOAST, "home"))
                     .setTitle("Home")
-                    .setSubtitle(homeSubtitle)
+                    .setSubtitle("2 hours 33 min via 101")
                     .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home),
                             getBroadcastIntent(ACTION_TOAST, "home")))
                 .build();
@@ -320,6 +330,86 @@
             .build();
     }
 
+    private Handler mHandler = new Handler();
+    private Runnable mLoader;
+    private boolean mLoaded = false;
+
+    private Slice createLoadingSlice(Uri sliceUri, boolean loadAll, boolean isList) {
+        if (!mLoaded || mLoader != null) {
+            // Need to load content or we're still loading so just return partial
+            if (!mLoaded) {
+                mLoader = () -> {
+                    // Note that we've loaded things
+                    mLoader = null;
+                    mLoaded = true;
+                    // Notify to update the slice
+                    getContext().getContentResolver().notifyChange(sliceUri, null);
+                };
+                mHandler.postDelayed(mLoader, LOADING_DELAY_MS);
+            }
+            if (loadAll) {
+                return isList
+                        ? new ListBuilder(getContext(), sliceUri).build()
+                        : new GridBuilder(getContext(), sliceUri).build();
+            }
+            return createPartialSlice(sliceUri, true, isList);
+        } else {
+            mLoaded = false;
+            return createPartialSlice(sliceUri, false, isList);
+        }
+    }
+
+    private Slice createPartialSlice(Uri sliceUri, boolean isPartial, boolean isList) {
+        Icon icon = Icon.createWithResource(getContext(), R.drawable.ic_star_on);
+        PendingIntent intent = getBroadcastIntent(ACTION_TOAST, "star tapped");
+        PendingIntent intent2 = getBroadcastIntent(ACTION_TOAST, "toggle tapped");
+        if (isPartial) {
+            if (isList) {
+                return new ListBuilder(getContext(), sliceUri)
+                        .addRow(b -> createRow(b, "Slice that has content to load",
+                                "Temporary subtitle", icon, intent, true))
+                        .addRow(b -> createRow(b, null, null, null, intent, true))
+                        .addRow(b -> b.setTitle("My title").addToggle(intent2, false, true))
+                        .build();
+            } else {
+                return new GridBuilder(getContext(), sliceUri)
+                        .addCell(b -> createCell(b, null, null, null, true))
+                        .addCell(b -> createCell(b, "Two stars", null, icon, true))
+                        .addCell(b -> createCell(b, null, null, null, true))
+                        .build();
+            }
+        } else {
+            if (isList) {
+                return new ListBuilder(getContext(), sliceUri)
+                        .addRow(b -> createRow(b, "Slice that has content to load",
+                                "Subtitle loaded", icon, intent, false))
+                        .addRow(b -> createRow(b, "Loaded row", "Loaded subtitle",
+                                icon, intent, false))
+                        .addRow(b -> b.setTitle("My title").addToggle(intent2, false))
+                        .build();
+            } else {
+                return new GridBuilder(getContext(), sliceUri)
+                        .addCell(b -> createCell(b, "One star", "meh", icon, false))
+                        .addCell(b -> createCell(b, "Two stars", "good", icon, false))
+                        .addCell(b -> createCell(b, "Three stars", "best", icon, false))
+                        .build();
+            }
+        }
+    }
+
+    private ListBuilder.RowBuilder createRow(ListBuilder.RowBuilder rb, String title,
+            String subtitle, Icon icon, PendingIntent content, boolean isLoading) {
+        return rb.setTitle(title, isLoading)
+          .setSubtitle(subtitle, isLoading)
+          .addEndItem(icon, isLoading)
+          .setContentIntent(content);
+    }
+
+    private GridBuilder.CellBuilder createCell(GridBuilder.CellBuilder cb, String text1,
+            String text2, Icon icon, boolean isLoading) {
+        return cb.addText(text1, isLoading).addText(text2, isLoading).addImage(icon, isLoading);
+    }
+
     private PendingIntent getIntent(String action) {
         Intent intent = new Intent(action);
         PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java
index 05f0820..24b2b77 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBroadcastReceiver.java
@@ -16,6 +16,8 @@
 
 package com.example.androidx.slice.demos;
 
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
+
 import static com.example.androidx.slice.demos.SampleSliceProvider.getUri;
 
 import android.content.BroadcastReceiver;
@@ -25,8 +27,6 @@
 import android.os.Handler;
 import android.widget.Toast;
 
-import androidx.app.slice.core.SliceHints;
-
 /**
  * Responds to actions performed on slices and notifies slices of updates in state changes.
  */
@@ -38,8 +38,7 @@
         switch (action) {
             case SampleSliceProvider.ACTION_WIFI_CHANGED:
                 WifiManager wm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
-                boolean newState = i.getBooleanExtra(SliceHints.EXTRA_TOGGLE_STATE,
-                        wm.isWifiEnabled());
+                boolean newState = i.getBooleanExtra(EXTRA_TOGGLE_STATE, wm.isWifiEnabled());
                 wm.setWifiEnabled(newState);
                 // Wait a bit for wifi to update (TODO: is there a better way to do this?)
                 Handler h = new Handler();
diff --git a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
index 3769f34..7015743 100644
--- a/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
+++ b/samples/SupportSliceDemos/src/main/java/com/example/androidx/slice/demos/SliceBrowser.java
@@ -66,6 +66,7 @@
 
     private static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI";
     private static final boolean TEST_INTENT = false;
+    private static final boolean TEST_THEMES = false;
 
     private ArrayList<Uri> mSliceUris = new ArrayList<Uri>();
     private int mSelectedMode;
@@ -122,8 +123,8 @@
         });
 
         mSelectedMode = (savedInstanceState != null)
-                ? savedInstanceState.getInt("SELECTED_MODE", SliceView.MODE_SHORTCUT)
-                : SliceView.MODE_SHORTCUT;
+                ? savedInstanceState.getInt("SELECTED_MODE", SliceView.MODE_LARGE)
+                : SliceView.MODE_LARGE;
         if (savedInstanceState != null) {
             mSearchView.setQuery(savedInstanceState.getString("SELECTED_QUERY"), true);
         }
@@ -139,7 +140,7 @@
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         mTypeMenu = menu.addSubMenu("Type");
-        mTypeMenu.setIcon(R.drawable.ic_shortcut);
+        mTypeMenu.setIcon(R.drawable.ic_large);
         mTypeMenu.getItem().setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
         mTypeMenu.add("Shortcut");
         mTypeMenu.add("Small");
@@ -220,7 +221,9 @@
     }
 
     private void addSlice(Intent intent) {
-        SliceView v = new SliceView(getApplicationContext());
+        SliceView v = TEST_THEMES
+                ? (SliceView) getLayoutInflater().inflate(R.layout.slice_view, mContainer, false)
+                : new SliceView(getApplicationContext());
         v.setOnSliceActionListener(this);
         v.setTag(intent);
         if (mSliceLiveData != null) {
@@ -235,7 +238,10 @@
 
     private void addSlice(Uri uri) {
         if (ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) {
-            SliceView v = new SliceView(getApplicationContext());
+            SliceView v = TEST_THEMES
+                    ? (SliceView) getLayoutInflater().inflate(
+                            R.layout.slice_view, mContainer, false)
+                    : new SliceView(getApplicationContext());
             v.setOnSliceActionListener(this);
             v.setTag(uri);
             if (mSliceLiveData != null) {
diff --git a/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml b/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
new file mode 100644
index 0000000..8d5eb61
--- /dev/null
+++ b/samples/SupportSliceDemos/src/main/res/layout/slice_view.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<androidx.app.slice.widget.SliceView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    style="@style/CustomSliceView"/>
\ No newline at end of file
diff --git a/samples/SupportSliceDemos/src/main/res/values/styles.xml b/samples/SupportSliceDemos/src/main/res/values/styles.xml
index afb6bad..a01e0aa 100644
--- a/samples/SupportSliceDemos/src/main/res/values/styles.xml
+++ b/samples/SupportSliceDemos/src/main/res/values/styles.xml
@@ -24,6 +24,7 @@
         <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
         <item name="colorAccent">@color/colorAccent</item>
     </style>
+
     <style name="AppTheme.NoActionBar">
         <item name="windowActionBar">false</item>
         <item name="windowNoTitle">true</item>
@@ -31,4 +32,14 @@
     <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
     <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
 
+    <style name="CustomSliceView" parent="Widget.SliceView">
+        <item name="titleColor">@color/colorAccent</item>
+        <item name="tintColor">@color/colorAccent</item>
+        <item name="headerTitleSize">20sp</item>
+        <item name="headerSubtitleSize">16sp</item>
+        <item name="android:paddingLeft">14dp</item>
+        <item name="android:paddingTop">8dp</item>
+        <item name="android:paddingRight">20dp</item>
+        <item name="android:paddingBottom">8dp</item>
+    </style>
 </resources>
diff --git a/slices/builders/api/current.txt b/slices/builders/api/current.txt
index eeb0d2f..513a054 100644
--- a/slices/builders/api/current.txt
+++ b/slices/builders/api/current.txt
@@ -11,9 +11,13 @@
     ctor public GridBuilder.CellBuilder(androidx.app.slice.builders.GridBuilder);
     ctor public GridBuilder.CellBuilder(androidx.app.slice.builders.GridBuilder, android.net.Uri);
     method public androidx.app.slice.builders.GridBuilder.CellBuilder addImage(android.graphics.drawable.Icon);
+    method public androidx.app.slice.builders.GridBuilder.CellBuilder addImage(android.graphics.drawable.Icon, boolean);
     method public androidx.app.slice.builders.GridBuilder.CellBuilder addLargeImage(android.graphics.drawable.Icon);
+    method public androidx.app.slice.builders.GridBuilder.CellBuilder addLargeImage(android.graphics.drawable.Icon, boolean);
     method public androidx.app.slice.builders.GridBuilder.CellBuilder addText(java.lang.CharSequence);
+    method public androidx.app.slice.builders.GridBuilder.CellBuilder addText(java.lang.CharSequence, boolean);
     method public androidx.app.slice.builders.GridBuilder.CellBuilder addTitleText(java.lang.CharSequence);
+    method public androidx.app.slice.builders.GridBuilder.CellBuilder addTitleText(java.lang.CharSequence, boolean);
     method public androidx.app.slice.builders.GridBuilder.CellBuilder setContentIntent(android.app.PendingIntent);
   }
 
@@ -49,15 +53,23 @@
     ctor public ListBuilder.RowBuilder(android.content.Context, android.net.Uri);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder addEndItem(long);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder addEndItem(android.graphics.drawable.Icon);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder addEndItem(android.graphics.drawable.Icon, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder addEndItem(android.graphics.drawable.Icon, android.app.PendingIntent);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder addEndItem(android.graphics.drawable.Icon, android.app.PendingIntent, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder addToggle(android.app.PendingIntent, boolean);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder addToggle(android.app.PendingIntent, boolean, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder addToggle(android.app.PendingIntent, boolean, android.graphics.drawable.Icon);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder addToggle(android.app.PendingIntent, boolean, android.graphics.drawable.Icon, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setContentIntent(android.app.PendingIntent);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setSubtitle(java.lang.CharSequence);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder setSubtitle(java.lang.CharSequence, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitle(java.lang.CharSequence);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitle(java.lang.CharSequence, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitleItem(long);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitleItem(android.graphics.drawable.Icon);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitleItem(android.graphics.drawable.Icon, boolean);
     method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitleItem(android.graphics.drawable.Icon, android.app.PendingIntent);
+    method public androidx.app.slice.builders.ListBuilder.RowBuilder setTitleItem(android.graphics.drawable.Icon, android.app.PendingIntent, boolean);
   }
 
   public abstract class TemplateSliceBuilder {
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
index 01db779..6f967cb 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/GridBuilder.java
@@ -24,6 +24,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RequiresApi;
 import android.support.annotation.RestrictTo;
 
@@ -162,7 +163,22 @@
          */
         @NonNull
         public CellBuilder addText(@NonNull CharSequence text) {
-            mImpl.addText(text);
+            return addText(text, false /* isLoading */);
+        }
+
+        /**
+         * Adds text to the cell. There can be at most two text items, the first two added
+         * will be used, others will be ignored.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public CellBuilder addText(@Nullable CharSequence text, boolean isLoading) {
+            mImpl.addText(text, isLoading);
             return this;
         }
 
@@ -173,7 +189,23 @@
          */
         @NonNull
         public CellBuilder addTitleText(@NonNull CharSequence text) {
-            mImpl.addTitleText(text);
+            return addTitleText(text, false /* isLoading */);
+        }
+
+        /**
+         * Adds text to the cell. Text added with this method will be styled as a title.
+         * There can be at most two text items, the first two added will be used, others
+         * will be ignored.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public CellBuilder addTitleText(@Nullable CharSequence text, boolean isLoading) {
+            mImpl.addTitleText(text, isLoading);
             return this;
         }
 
@@ -185,7 +217,22 @@
          */
         @NonNull
         public CellBuilder addLargeImage(@NonNull Icon image) {
-            mImpl.addLargeImage(image);
+            return addLargeImage(image, false /* isLoading */);
+        }
+
+        /**
+         * Adds an image to the cell that should be displayed as large as the cell allows.
+         * There can be at most one image, the first one added will be used, others will be ignored.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public CellBuilder addLargeImage(@Nullable Icon image, boolean isLoading) {
+            mImpl.addLargeImage(image, isLoading);
             return this;
         }
 
@@ -197,7 +244,22 @@
          */
         @NonNull
         public CellBuilder addImage(@NonNull Icon image) {
-            mImpl.addImage(image);
+            return addImage(image, false /* isLoading */);
+        }
+
+        /**
+         * Adds an image to the cell. There can be at most one image, the first one added
+         * will be used, others will be ignored.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public CellBuilder addImage(@Nullable Icon image, boolean isLoading) {
+            mImpl.addImage(image, isLoading);
             return this;
         }
 
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
index 722543c..78f7c03 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/ListBuilder.java
@@ -273,10 +273,8 @@
         }
 
         /**
-         * Sets the title item to be the provided icon.
-         * <p>
-         * There can only be one title item, this will replace any other title
-         * items that may have been set.
+         * Sets the title item to be the provided icon. There can only be one title item, this
+         * will replace any other title items that may have been set.
          */
         @NonNull
         public RowBuilder setTitleItem(@NonNull Icon icon) {
@@ -285,14 +283,44 @@
         }
 
         /**
-         * Sets the title item to be a tappable icon.
+         * Sets the title item to be the provided icon. There can only be one title item, this
+         * will replace any other title items that may have been set.
          * <p>
-         * There can only be one title item, this will replace any other title
-         * items that may have been set.
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder setTitleItem(@Nullable Icon icon, boolean isLoading) {
+            mImpl.setTitleItem(icon, isLoading);
+            return this;
+        }
+
+        /**
+         * Sets the title item to be a tappable icon. There can only be one title item, this will
+         * replace any other title items that may have been set.
          */
         @NonNull
         public RowBuilder setTitleItem(@NonNull Icon icon, @NonNull PendingIntent action) {
-            mImpl.setTitleItem(icon, action);
+            return setTitleItem(icon, action, false /* isLoading */);
+        }
+
+        /**
+         * Sets the title item to be a tappable icon. There can only be one title item, this will
+         * replace any other title items that may have been set.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder setTitleItem(@NonNull Icon icon, @NonNull PendingIntent action,
+                boolean isLoading) {
+            mImpl.setTitleItem(icon, action, isLoading);
             return this;
         }
 
@@ -309,17 +337,46 @@
          * Sets the title text.
          */
         @NonNull
-        public RowBuilder setTitle(CharSequence title) {
+        public RowBuilder setTitle(@NonNull CharSequence title) {
             mImpl.setTitle(title);
             return this;
         }
 
         /**
+         * Sets the title text.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder setTitle(@Nullable CharSequence title, boolean isLoading) {
+            mImpl.setTitle(title, isLoading);
+            return this;
+        }
+
+        /**
          * Sets the subtitle text.
          */
         @NonNull
-        public RowBuilder setSubtitle(CharSequence subtitle) {
-            mImpl.setSubtitle(subtitle);
+        public RowBuilder setSubtitle(@NonNull CharSequence subtitle) {
+            return setSubtitle(subtitle, false /* isLoading */);
+        }
+
+        /**
+         * Sets the subtitle text.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder setSubtitle(@Nullable CharSequence subtitle, boolean isLoading) {
+            mImpl.setSubtitle(subtitle, isLoading);
             return this;
         }
 
@@ -345,12 +402,28 @@
          */
         @NonNull
         public RowBuilder addEndItem(@NonNull Icon icon) {
+            return addEndItem(icon, false /* isLoading */);
+        }
+
+        /**
+         * Adds an icon to be displayed at the end of the row. A mixture of icons and tappable
+         * icons is not permitted. If an action has already been added this will throw
+         * {@link IllegalArgumentException}.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder addEndItem(@NonNull Icon icon, boolean isLoading) {
             if (mHasEndActionOrToggle) {
                 throw new IllegalArgumentException("Trying to add an icon to end items when an"
                         + "action has already been added. End items cannot have a mixture of "
                         + "tappable icons and icons.");
             }
-            mImpl.addEndItem(icon);
+            mImpl.addEndItem(icon, isLoading);
             mHasEndImage = true;
             return this;
         }
@@ -362,13 +435,24 @@
          */
         @NonNull
         public RowBuilder addEndItem(@NonNull Icon icon, @NonNull PendingIntent action) {
-            if (mHasEndImage) {
-                throw new IllegalArgumentException("Trying to add an action to end items when an"
-                        + "icon has already been added. End items cannot have a mixture of "
-                        + "tappable icons and icons.");
-            }
-            mImpl.addEndItem(icon, action);
-            mHasEndActionOrToggle = true;
+            return addEndItem(icon, action, false /* isLoading */);
+        }
+
+        /**
+         * Adds a tappable icon to be displayed at the end of the row. A mixture of icons and
+         * tappable icons is not permitted. If an icon has already been added, this will throw
+         * {@link IllegalArgumentException}.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder addEndItem(@Nullable Icon icon, @Nullable PendingIntent action,
+                boolean isLoading) {
+            mImpl.addEndItem(icon, action, isLoading);
             return this;
         }
 
@@ -379,7 +463,24 @@
          */
         @NonNull
         public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked) {
-            return addToggleInternal(action, isChecked, null);
+            return addToggle(action, isChecked, null, false /* isLoading */);
+        }
+
+        /**
+         * Adds a toggle action to be displayed at the end of the row. A mixture of icons and
+         * tappable icons is not permitted. If an icon has already been added, this will throw an
+         * {@link IllegalArgumentException}.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked,
+                boolean isLoading) {
+            return addToggleInternal(action, isChecked, null, isLoading);
         }
 
         /**
@@ -391,11 +492,29 @@
         @NonNull
         public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked,
                 @NonNull Icon icon) {
-            return addToggleInternal(action, isChecked, icon);
+            return addToggle(action, isChecked, icon, false /* isLoading */);
+        }
+
+        /**
+         * Adds a toggle action to be displayed with custom icons to represent checked and
+         * unchecked state at the end of the row. A mixture of icons and tappable icons is not
+         * permitted. If an icon has already been added, this will throw an
+         * {@link IllegalArgumentException}.
+         * <p>
+         * Use this method to specify content that will appear in the template once it's been
+         * loaded.
+         * </p>
+         * @param isLoading indicates whether the app is doing work to load the added content in the
+         *                  background or not.
+         */
+        @NonNull
+        public RowBuilder addToggle(@NonNull PendingIntent action, boolean isChecked,
+                @NonNull Icon icon, boolean isLoading) {
+            return addToggleInternal(action, isChecked, icon, isLoading);
         }
 
         private RowBuilder addToggleInternal(@NonNull PendingIntent action, boolean isChecked,
-                @Nullable Icon icon) {
+                @Nullable Icon icon, boolean isLoading) {
             if (mHasEndImage) {
                 throw new IllegalStateException("Trying to add a toggle to end items when an "
                         + "icon has already been added. End items cannot have a mixture of "
@@ -406,7 +525,7 @@
                         + "in a single row. If you would like to include multiple toggles "
                         + "in a row, set a custom icon for each toggle.");
             }
-            mImpl.addToggle(action, isChecked, icon);
+            mImpl.addToggle(action, isChecked, icon, isLoading);
             mHasDefaultToggle = icon == null;
             mHasEndActionOrToggle = true;
             return this;
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
index 28134be..91266a4 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilder.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 
 import androidx.app.slice.Slice;
@@ -62,6 +63,17 @@
         void addText(@NonNull CharSequence text);
 
         /**
+         * Adds text to the cell. There can be at most two text items, the first two added
+         * will be used, others will be ignored.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        @NonNull
+        void addText(@Nullable CharSequence text, boolean isLoading);
+
+        /**
          * Adds text to the cell. Text added with this method will be styled as a title.
          * There can be at most two text items, the first two added will be used, others
          * will be ignored.
@@ -70,6 +82,18 @@
         void addTitleText(@NonNull CharSequence text);
 
         /**
+         * Adds text to the cell. Text added with this method will be styled as a title.
+         * There can be at most two text items, the first two added will be used, others
+         * will be ignored.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        @NonNull
+        void addTitleText(@Nullable CharSequence text, boolean isLoading);
+
+        /**
          * Adds an image to the cell that should be displayed as large as the cell allows.
          * There can be at most one image, the first one added will be used, others will be ignored.
          *
@@ -79,6 +103,17 @@
         void addLargeImage(@NonNull Icon image);
 
         /**
+         * Adds an image to the cell that should be displayed as large as the cell allows.
+         * There can be at most one image, the first one added will be used, others will be ignored.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        @NonNull
+        void addLargeImage(@Nullable Icon image, boolean isLoading);
+
+        /**
          * Adds an image to the cell. There can be at most one image, the first one added
          * will be used, others will be ignored.
          *
@@ -88,6 +123,17 @@
         void addImage(@NonNull Icon image);
 
         /**
+         * Adds an image to the cell. There can be at most one image, the first one added
+         * will be used, others will be ignored.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.l.
+         */
+        @NonNull
+        void addImage(@NonNull Icon image, boolean isLoading);
+
+        /**
          * Sets the action to be invoked if the user taps on this cell in the row.
          */
         @NonNull
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
index 8205013..63f7506 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderBasicImpl.java
@@ -22,6 +22,7 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 
 import androidx.app.slice.Slice;
@@ -102,6 +103,12 @@
 
         /**
          */
+        @Override
+        public void addText(@Nullable CharSequence text, boolean isLoading) {
+        }
+
+        /**
+         */
         @NonNull
         @Override
         public void addTitleText(@NonNull CharSequence text) {
@@ -111,6 +118,13 @@
          */
         @NonNull
         @Override
+        public void addTitleText(@Nullable CharSequence text, boolean isLoading) {
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
         public void addLargeImage(@NonNull Icon image) {
         }
 
@@ -118,6 +132,13 @@
          */
         @NonNull
         @Override
+        public void addLargeImage(@Nullable Icon image, boolean isLoading) {
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
         public void addImage(@NonNull Icon image) {
         }
 
@@ -125,6 +146,13 @@
          */
         @NonNull
         @Override
+        public void addImage(@Nullable Icon image, boolean isLoading) {
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
         public void setContentIntent(@NonNull PendingIntent intent) {
         }
 
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
index d1a9e12..140bd9b 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/GridBuilderListV1Impl.java
@@ -19,12 +19,14 @@
 import static android.app.slice.Slice.HINT_HORIZONTAL;
 import static android.app.slice.Slice.HINT_LARGE;
 import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.support.annotation.RestrictTo.Scope.LIBRARY;
 
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 
 import androidx.app.slice.Slice;
@@ -103,7 +105,17 @@
         @NonNull
         @Override
         public void addText(@NonNull CharSequence text) {
-            getBuilder().addText(text, null);
+            addText(text, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void addText(@Nullable CharSequence text, boolean isLoading) {
+            @Slice.SliceHint String[] hints = isLoading
+                    ? new String[] {HINT_PARTIAL}
+                    : new String[0];
+            getBuilder().addText(text, null, hints);
         }
 
         /**
@@ -111,7 +123,18 @@
         @NonNull
         @Override
         public void addTitleText(@NonNull CharSequence text) {
-            getBuilder().addText(text, null, HINT_LARGE);
+            addTitleText(text, false /* isLoading */);
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
+        public void addTitleText(@Nullable CharSequence text, boolean isLoading) {
+            @Slice.SliceHint String[] hints = isLoading
+                    ? new String[] {HINT_PARTIAL, HINT_LARGE}
+                    : new String[] {HINT_LARGE};
+            getBuilder().addText(text, null, hints);
         }
 
         /**
@@ -119,7 +142,18 @@
         @NonNull
         @Override
         public void addLargeImage(@NonNull Icon image) {
-            getBuilder().addIcon(image, null, HINT_LARGE);
+            addLargeImage(image, false /* isLoading */);
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
+        public void addLargeImage(@Nullable Icon image, boolean isLoading) {
+            @Slice.SliceHint String[] hints = isLoading
+                    ? new String[] {HINT_PARTIAL, HINT_LARGE}
+                    : new String[] {HINT_LARGE};
+            getBuilder().addIcon(image, null, hints);
         }
 
         /**
@@ -127,7 +161,18 @@
         @NonNull
         @Override
         public void addImage(@NonNull Icon image) {
-            getBuilder().addIcon(image, null);
+            addImage(image, false /* isLoading */);
+        }
+
+        /**
+         */
+        @NonNull
+        @Override
+        public void addImage(@Nullable Icon image, boolean isLoading) {
+            @Slice.SliceHint String[] hints = isLoading
+                    ? new String[] {HINT_PARTIAL}
+                    : new String[0];
+            getBuilder().addIcon(image, null, hints);
         }
 
         /**
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
index 4aca4ee..9c2fd72 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilder.java
@@ -100,22 +100,38 @@
         void setTitleItem(long timeStamp);
 
         /**
-         * Sets the title item to be the provided icon.
-         * <p>
-         * There can only be one title item, this will replace any other title
-         * items that may have been set.
+         * Sets the title item to be the provided icon. There can only be one title item, this
+         * will replace any other title items that may have been set.
          */
         void setTitleItem(Icon icon);
 
         /**
-         * Sets the title item to be a tappable icon.
+         * Sets the title item to be the provided icon. There can only be one title item, this
+         * will replace any other title items that may have been set.
          * <p>
-         * There can only be one title item, this will replace any other title
-         * items that may have been set.
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void setTitleItem(Icon icon, boolean isLoading);
+
+        /**
+         * Sets the title item to be a tappable icon. There can only be one title item, this will
+         * replace any other title items that may have been set.
          */
         void setTitleItem(Icon icon, PendingIntent action);
 
         /**
+         * Sets the title item to be a tappable icon. There can only be one title item, this will
+         * replace any other title items that may have been set.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void setTitleItem(Icon icon, PendingIntent action, boolean isLoading);
+
+        /**
          * Sets the action to be invoked if the user taps on the main content of the template.
          */
         void setContentIntent(PendingIntent action);
@@ -126,11 +142,29 @@
         void setTitle(CharSequence title);
 
         /**
+         * Sets the title text.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void setTitle(CharSequence title, boolean isLoading);
+
+        /**
          * Sets the subtitle text.
          */
         void setSubtitle(CharSequence subtitle);
 
         /**
+         * Sets the subtitle text.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void setSubtitle(CharSequence subtitle, boolean isLoading);
+
+        /**
          * Adds a timestamp to be displayed at the end of the row.
          */
         void addEndItem(long timeStamp);
@@ -141,15 +175,43 @@
         void addEndItem(Icon icon);
 
         /**
+         * Adds an icon to be displayed at the end of the row.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void addEndItem(Icon icon, boolean isLoading);
+
+        /**
          * Adds a tappable icon to be displayed at the end of the row.
          */
         void addEndItem(Icon icon, PendingIntent action);
 
         /**
+         * Adds a tappable icon to be displayed at the end of the row.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void addEndItem(Icon icon, PendingIntent action, boolean isLoading);
+
+        /**
          * Adds a toggle action to the template with custom icons to represent checked and unchecked
          * state.
          */
         void addToggle(PendingIntent action, boolean isChecked, Icon icon);
+
+        /**
+         * Adds a toggle action to the template with custom icons to represent checked and unchecked
+         * state.
+         * <p>
+         * When set to true, the parameter {@code isLoading} indicates that the app is doing work
+         * to load this content in the background, in this case the template displays a placeholder
+         * until updated.
+         */
+        void addToggle(PendingIntent action, boolean isChecked, Icon icon, boolean isLoading);
     }
 
 
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
index 6987adb..6520a16 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderBasicImpl.java
@@ -153,6 +153,13 @@
         /**
          */
         @Override
+        public void addEndItem(Icon icon, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void addEndItem(Icon icon, PendingIntent action) {
 
         }
@@ -160,6 +167,13 @@
         /**
          */
         @Override
+        public void addEndItem(Icon icon, PendingIntent action, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void addToggle(PendingIntent action, boolean isChecked, Icon icon) {
 
         }
@@ -167,6 +181,14 @@
         /**
          */
         @Override
+        public void addToggle(PendingIntent action, boolean isChecked, Icon icon,
+                boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void setTitleItem(long timeStamp) {
 
         }
@@ -181,6 +203,13 @@
         /**
          */
         @Override
+        public void setTitleItem(Icon icon, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void setTitleItem(Icon icon, PendingIntent action) {
             mIcon = icon;
         }
@@ -188,6 +217,13 @@
         /**
          */
         @Override
+        public void setTitleItem(Icon icon, PendingIntent action, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void setContentIntent(PendingIntent action) {
 
         }
@@ -202,6 +238,13 @@
         /**
          */
         @Override
+        public void setTitle(CharSequence title, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void setSubtitle(CharSequence subtitle) {
             mSubtitle = subtitle;
         }
@@ -209,6 +252,13 @@
         /**
          */
         @Override
+        public void setSubtitle(CharSequence subtitle, boolean isLoading) {
+
+        }
+
+        /**
+         */
+        @Override
         public void addEndItem(long timeStamp) {
 
         }
diff --git a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
index 38d7fc3..9d7fba4 100644
--- a/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
+++ b/slices/builders/src/main/java/androidx/app/slice/builders/impl/ListBuilderV1Impl.java
@@ -20,23 +20,25 @@
 import static android.app.slice.Slice.HINT_LARGE;
 import static android.app.slice.Slice.HINT_LIST_ITEM;
 import static android.app.slice.Slice.HINT_NO_TINT;
+import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.Slice.HINT_SELECTED;
+import static android.app.slice.Slice.HINT_SUMMARY;
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.Slice.SUBTYPE_COLOR;
 import static android.app.slice.Slice.SUBTYPE_CONTENT_DESCRIPTION;
 import static android.app.slice.Slice.SUBTYPE_PRIORITY;
+import static android.app.slice.Slice.SUBTYPE_TOGGLE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 import static android.support.annotation.RestrictTo.Scope.LIBRARY;
 
-import static androidx.app.slice.core.SliceHints.SUBTYPE_TOGGLE;
-
 import android.app.PendingIntent;
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 
 import java.util.ArrayList;
@@ -44,7 +46,6 @@
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceItem;
 import androidx.app.slice.SliceSpec;
-import androidx.app.slice.core.SliceHints;
 
 /**
  * @hide
@@ -197,7 +198,17 @@
         @NonNull
         @Override
         public void setTitleItem(@NonNull Icon icon) {
+            setTitleItem(icon, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void setTitleItem(@Nullable Icon icon, boolean isLoading) {
             mStartItem = new SliceItem(icon, FORMAT_IMAGE, null, new String[0]);
+            if (isLoading) {
+                mStartItem.addHint(HINT_PARTIAL);
+            }
         }
 
         /**
@@ -205,8 +216,18 @@
         @NonNull
         @Override
         public void setTitleItem(@NonNull Icon icon, @NonNull PendingIntent action) {
+            setTitleItem(icon, action, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void setTitleItem(Icon icon, PendingIntent action, boolean isLoading) {
             Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
             mStartItem = new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]);
+            if (isLoading) {
+                mStartItem.addHint(HINT_PARTIAL);
+            }
         }
 
         /**
@@ -222,7 +243,17 @@
         @NonNull
         @Override
         public void setTitle(CharSequence title) {
-            mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[]{HINT_TITLE});
+            setTitle(title, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void setTitle(CharSequence title, boolean isLoading) {
+            mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
+            if (isLoading) {
+                mTitleItem.addHint(HINT_PARTIAL);
+            }
         }
 
         /**
@@ -230,7 +261,17 @@
         @NonNull
         @Override
         public void setSubtitle(CharSequence subtitle) {
+            setSubtitle(subtitle, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void setSubtitle(CharSequence subtitle, boolean isLoading) {
             mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
+            if (isLoading) {
+                mSubtitleItem.addHint(HINT_PARTIAL);
+            }
         }
 
         /**
@@ -246,8 +287,19 @@
         @NonNull
         @Override
         public void addEndItem(@NonNull Icon icon) {
-            mEndItems.add(new SliceItem(icon, FORMAT_IMAGE, null,
-                    new String[]{HINT_NO_TINT, HINT_LARGE}));
+            addEndItem(icon, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void addEndItem(Icon icon, boolean isLoading) {
+            SliceItem item = new SliceItem(icon, FORMAT_IMAGE, null,
+                    new String[] {HINT_NO_TINT, HINT_LARGE});
+            if (isLoading) {
+                item.addHint(HINT_PARTIAL);
+            }
+            mEndItems.add(item);
         }
 
         /**
@@ -255,8 +307,20 @@
         @NonNull
         @Override
         public void addEndItem(@NonNull Icon icon, @NonNull PendingIntent action) {
+            addEndItem(icon, action, false /* isLoading */);
+        }
+
+        /**
+         */
+        @Override
+        public void addEndItem(Icon icon, PendingIntent action, boolean isLoading) {
             Slice actionSlice = new Slice.Builder(getBuilder()).addIcon(icon, null).build();
-            mEndItems.add(new SliceItem(action, actionSlice, FORMAT_ACTION, null, new String[0]));
+            SliceItem item = new SliceItem(action, actionSlice, FORMAT_ACTION, null,
+                    new String[0]);
+            if (isLoading) {
+                item.addHint(HINT_PARTIAL);
+            }
+            mEndItems.add(item);
         }
 
         /**
@@ -265,15 +329,26 @@
         @Override
         public void addToggle(@NonNull PendingIntent action, boolean isChecked,
                 @NonNull Icon icon) {
+            addToggle(action, isChecked, icon, false /* isLoading */);
+        }
+
+        @Override
+        public void addToggle(PendingIntent action, boolean isChecked, Icon icon,
+                boolean isLoading) {
             @Slice.SliceHint String[] hints = isChecked
-                    ? new String[] {SUBTYPE_TOGGLE, HINT_SELECTED}
-                    : new String[] {SUBTYPE_TOGGLE};
+                    ? new String[] {HINT_SELECTED}
+                    : new String[0];
             Slice.Builder actionSliceBuilder = new Slice.Builder(getBuilder()).addHints(hints);
             if (icon != null) {
                 actionSliceBuilder.addIcon(icon, null);
             }
             Slice actionSlice = actionSliceBuilder.build();
-            mEndItems.add(new SliceItem(action, actionSlice, FORMAT_ACTION, null, hints));
+            SliceItem item = new SliceItem(action, actionSlice, FORMAT_ACTION, SUBTYPE_TOGGLE,
+                    hints);
+            if (isLoading) {
+                item.addHint(HINT_PARTIAL);
+            }
+            mEndItems.add(item);
         }
 
         /**
@@ -341,7 +416,7 @@
                 b.addText(mSubtitle, null /* subtype */);
             }
             if (mSummarySubtitle != null) {
-                b.addText(mSummarySubtitle, null /* subtype */, SliceHints.HINT_SUMMARY);
+                b.addText(mSummarySubtitle, null /* subtype */, HINT_SUMMARY);
             }
             if (mContentIntent != null) {
                 wrapped.addAction(mContentIntent, b.build(), null /* subtype */);
diff --git a/slices/core/src/main/java/androidx/app/slice/Slice.java b/slices/core/src/main/java/androidx/app/slice/Slice.java
index 4681b2d..153a34f 100644
--- a/slices/core/src/main/java/androidx/app/slice/Slice.java
+++ b/slices/core/src/main/java/androidx/app/slice/Slice.java
@@ -24,6 +24,7 @@
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.Slice.HINT_SELECTED;
+import static android.app.slice.Slice.HINT_SUMMARY;
 import static android.app.slice.Slice.HINT_TITLE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
@@ -57,7 +58,6 @@
 import java.util.List;
 
 import androidx.app.slice.compat.SliceProviderCompat;
-import androidx.app.slice.core.SliceHints;
 
 /**
  * A slice is a piece of app content and actions that can be surfaced outside of the app.
@@ -80,8 +80,7 @@
      */
     @RestrictTo(Scope.LIBRARY)
     @StringDef({HINT_TITLE, HINT_LIST, HINT_LIST_ITEM, HINT_LARGE, HINT_ACTIONS, HINT_SELECTED,
-            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL,
-            SliceHints.HINT_SUMMARY, SliceHints.SUBTYPE_TOGGLE})
+            HINT_HORIZONTAL, HINT_NO_TINT, HINT_PARTIAL, HINT_SUMMARY})
     public @interface SliceHint{ }
 
     private final SliceItem[] mItems;
diff --git a/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
index c98f1d2..cb913e2 100644
--- a/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
+++ b/slices/core/src/main/java/androidx/app/slice/core/SliceHints.java
@@ -18,7 +18,6 @@
 
 import static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
 
-import android.app.slice.Slice;
 import android.support.annotation.RestrictTo;
 
 /**
@@ -28,14 +27,6 @@
 @RestrictTo(LIBRARY_GROUP)
 public class SliceHints {
     /**
-     * Subtype to indicate that this content has a toggle action associated with it. To indicate
-     * that the toggle is on, use {@link Slice#HINT_SELECTED}. When the toggle state changes, the
-     * intent associated with it will be sent along with an extra {@link #EXTRA_TOGGLE_STATE}
-     * which can be retrieved to see the new state of the toggle.
-     */
-    public static final String SUBTYPE_TOGGLE = "toggle";
-
-    /**
      * Subtype indicating that this content is the maximum value for a slider or progress.
      */
     public static final String SUBTYPE_MAX = "max";
@@ -46,19 +37,7 @@
     public static final String SUBTYPE_PROGRESS = "progress";
 
     /**
-     * Key to retrieve an extra added to an intent when a control is changed.
-     */
-    public static final String EXTRA_TOGGLE_STATE = "android.app.slice.extra.TOGGLE_STATE";
-
-    /**
      * Key to retrieve an extra added to an intent when the value of a slider has changed.
      */
     public static final String EXTRA_SLIDER_VALUE = "android.app.slice.extra.SLIDER_VALUE";
-
-    /**
-     * Hint indicating this content should be shown instead of the normal content when the slice
-     * is in small format
-     */
-    public static final String HINT_SUMMARY = "summary";
-
 }
diff --git a/slices/view/api/current.txt b/slices/view/api/current.txt
index 251742a..d158818 100644
--- a/slices/view/api/current.txt
+++ b/slices/view/api/current.txt
@@ -16,8 +16,12 @@
   }
 
   public class SliceUtils {
+    method public static int getLoadingState(androidx.app.slice.Slice);
     method public static androidx.app.slice.Slice parseSlice(java.io.InputStream, java.lang.String) throws java.io.IOException;
     method public static void serializeSlice(androidx.app.slice.Slice, android.content.Context, java.io.OutputStream, java.lang.String, androidx.app.slice.SliceUtils.SerializeOptions) throws java.io.IOException;
+    field public static final int LOADING_ALL = 0; // 0x0
+    field public static final int LOADING_COMPLETE = 2; // 0x2
+    field public static final int LOADING_PARTIAL = 1; // 0x1
   }
 
   public static class SliceUtils.SerializeOptions {
@@ -76,6 +80,7 @@
     method public void setOnSliceActionListener(androidx.app.slice.widget.SliceView.OnSliceActionListener);
     method public void setScrollable(boolean);
     method public void setSlice(androidx.app.slice.Slice);
+    method public void setTint(int);
     field public static final int MODE_LARGE = 2; // 0x2
     field public static final int MODE_SHORTCUT = 3; // 0x3
     field public static final int MODE_SMALL = 1; // 0x1
diff --git a/slices/view/build.gradle b/slices/view/build.gradle
index 92e117e..93e9fc5 100644
--- a/slices/view/build.gradle
+++ b/slices/view/build.gradle
@@ -29,6 +29,7 @@
     api(ARCH_LIFECYCLE_EXTENSIONS, libs.exclude_annotations_transitive)
 
     androidTestImplementation(TEST_RUNNER)
+    androidTestImplementation(ESPRESSO_CORE)
     androidTestImplementation(MOCKITO_CORE, libs.exclude_bytebuddy)
     androidTestImplementation(DEXMAKER_MOCKITO, libs.exclude_bytebuddy)
 }
diff --git a/slices/view/src/androidTest/AndroidManifest.xml b/slices/view/src/androidTest/AndroidManifest.xml
index 5a7abb6..6800706 100644
--- a/slices/view/src/androidTest/AndroidManifest.xml
+++ b/slices/view/src/androidTest/AndroidManifest.xml
@@ -16,9 +16,22 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="androidx.app.slice.view.test">
 
+    <uses-sdk android:targetSdkVersion="21" />
+
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
     <application>
         <provider android:name="androidx.app.slice.SliceManagerTest$TestSliceProvider"
                   android:authorities="androidx.app.slice.view.test"
                   android:exported="true"/>
+
+        <activity android:name="androidx.app.slice.render.SliceRenderActivity"
+            android:theme="@style/AppTheme.NoActionBar">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest>
diff --git a/slices/view/src/androidTest/java/androidx/app/slice/render/RenderTest.java b/slices/view/src/androidTest/java/androidx/app/slice/render/RenderTest.java
new file mode 100644
index 0000000..6cf6182
--- /dev/null
+++ b/slices/view/src/androidTest/java/androidx/app/slice/render/RenderTest.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.app.slice.render;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class RenderTest {
+
+    private final Context mContext = InstrumentationRegistry.getContext();
+
+    @Test
+    public void testRender() throws InterruptedException {
+        final CountDownLatch latch = new CountDownLatch(1);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                latch.countDown();
+            }
+        };
+        mContext.registerReceiver(receiver,
+                new IntentFilter(SliceRenderActivity.ACTION_RENDER_DONE));
+        mContext.startActivity(new Intent(mContext, SliceRenderActivity.class)
+                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
+
+        latch.await(30000, TimeUnit.MILLISECONDS);
+    }
+}
diff --git a/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java
new file mode 100644
index 0000000..114d0e1
--- /dev/null
+++ b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceCreator.java
@@ -0,0 +1,307 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.app.slice.render;
+
+import static android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE;
+
+import android.app.PendingIntent;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.text.SpannableString;
+import android.text.format.DateUtils;
+import android.text.style.ForegroundColorSpan;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.builders.GridBuilder;
+import androidx.app.slice.builders.ListBuilder;
+import androidx.app.slice.builders.MessagingSliceBuilder;
+import androidx.app.slice.view.test.R;
+
+/**
+ * Examples of using slice template builders.
+ */
+public class SliceCreator {
+
+    public static final String ACTION_WIFI_CHANGED =
+            "com.example.androidx.slice.action.WIFI_CHANGED";
+    public static final String ACTION_TOAST =
+            "com.example.androidx.slice.action.TOAST";
+    public static final String EXTRA_TOAST_MESSAGE = "com.example.androidx.extra.TOAST_MESSAGE";
+
+    public static final String[] URI_PATHS = {"message", "wifi", "note", "ride", "toggle",
+            "toggle2", "contact", "gallery", "weather"};
+
+    private final Context mContext;
+
+    public SliceCreator(Context context) {
+        mContext = context;
+    }
+
+    private Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * @return Uri with the provided path
+     */
+    public static Uri getUri(String path, Context context) {
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_CONTENT)
+                .authority("androidx.app.slice.view.test")
+                .appendPath(path)
+                .build();
+    }
+
+    public Slice onBindSlice(Uri sliceUri) {
+        String path = sliceUri.getPath();
+        switch (path) {
+            case "/message":
+                return createMessagingSlice(sliceUri);
+            case "/wifi":
+                return createWifiSlice(sliceUri);
+            case "/note":
+                return createNoteSlice(sliceUri);
+            case "/ride":
+                return createRideSlice(sliceUri);
+            case "/toggle":
+                return createCustomToggleSlice(sliceUri);
+            case "/toggle2":
+                return createTwoCustomToggleSlices(sliceUri);
+            case "/contact":
+                return createContact(sliceUri);
+            case "/gallery":
+                return createGallery(sliceUri);
+            case "/weather":
+                return createWeather(sliceUri);
+        }
+        throw new IllegalArgumentException("Unknown uri " + sliceUri);
+    }
+
+    private Slice createWeather(Uri sliceUri) {
+        GridBuilder b = new GridBuilder(getContext(), sliceUri);
+        return b.addCell(new GridBuilder.CellBuilder(b)
+                        .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1))
+                        .addText("MON")
+                        .addTitleText("69\u00B0"))
+                .addCell(new GridBuilder.CellBuilder(b)
+                        .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_2))
+                        .addText("TUE")
+                        .addTitleText("71\u00B0"))
+                .addCell(new GridBuilder.CellBuilder(b)
+                        .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_3))
+                        .addText("WED")
+                        .addTitleText("76\u00B0"))
+                .addCell(new GridBuilder.CellBuilder(b)
+                        .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_4))
+                        .addText("THU")
+                        .addTitleText("72\u00B0"))
+                .addCell(new GridBuilder.CellBuilder(b)
+                        .addLargeImage(Icon.createWithResource(getContext(), R.drawable.weather_1))
+                        .addText("FRI")
+                        .addTitleText("68\u00B0"))
+                .build();
+    }
+
+    private Slice createGallery(Uri sliceUri) {
+        GridBuilder b = new GridBuilder(getContext(), sliceUri);
+        return b.addCell(new GridBuilder.CellBuilder(b)
+                    .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_1)))
+                .addCell(new GridBuilder.CellBuilder(b)
+                    .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_2)))
+                .addCell(new GridBuilder.CellBuilder(b)
+                    .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_3)))
+                .addCell(new GridBuilder.CellBuilder(b)
+                    .addLargeImage(Icon.createWithResource(getContext(), R.drawable.slices_4)))
+                .build();
+    }
+
+    private Slice createContact(Uri sliceUri) {
+        ListBuilder b = new ListBuilder(getContext(), sliceUri);
+        ListBuilder.RowBuilder rb = new ListBuilder.RowBuilder(b);
+        GridBuilder gb = new GridBuilder(b);
+        return b.setColor(0xff3949ab)
+                .addRow(rb
+                        .setTitle("Mady Pitza")
+                        .setSubtitle("Frequently contacted contact")
+                        .addEndItem(Icon.createWithResource(getContext(), R.drawable.mady)))
+                .addGrid(gb
+                        .addCell(new GridBuilder.CellBuilder(gb)
+                            .addImage(Icon.createWithResource(getContext(), R.drawable.ic_call))
+                            .addText("Call")
+                            .setContentIntent(getBroadcastIntent(ACTION_TOAST, "call")))
+                        .addCell(new GridBuilder.CellBuilder(gb)
+                            .addImage(Icon.createWithResource(getContext(), R.drawable.ic_text))
+                            .addText("Text")
+                            .setContentIntent(getBroadcastIntent(ACTION_TOAST, "text")))
+                        .addCell(new GridBuilder.CellBuilder(gb)
+                            .addImage(Icon.createWithResource(getContext(), R.drawable.ic_video))
+                            .setContentIntent(getBroadcastIntent(ACTION_TOAST, "video"))
+                            .addText("Video"))
+                        .addCell(new GridBuilder.CellBuilder(gb)
+                            .addImage(Icon.createWithResource(getContext(), R.drawable.ic_email))
+                            .addText("Email")
+                            .setContentIntent(getBroadcastIntent(ACTION_TOAST, "email"))))
+                .build();
+    }
+
+    private Slice createMessagingSlice(Uri sliceUri) {
+        // TODO: Remote input.
+        MessagingSliceBuilder mb = new MessagingSliceBuilder(getContext(), sliceUri);
+        return mb
+                .add(new MessagingSliceBuilder.MessageBuilder(mb)
+                        .addText("yo home \uD83C\uDF55, I emailed you the info")
+                        .addTimestamp(System.currentTimeMillis() - 20 * DateUtils.MINUTE_IN_MILLIS)
+                        .addSource(Icon.createWithResource(getContext(), R.drawable.mady)))
+                .add(new MessagingSliceBuilder.MessageBuilder(mb)
+                        .addText("just bought my tickets")
+                        .addTimestamp(System.currentTimeMillis() - 10 * DateUtils.MINUTE_IN_MILLIS))
+                .add(new MessagingSliceBuilder.MessageBuilder(mb)
+                        .addText("yay! can't wait for getContext() weekend!\n"
+                                + "\uD83D\uDE00")
+                        .addTimestamp(System.currentTimeMillis() - 5 * DateUtils.MINUTE_IN_MILLIS)
+                        .addSource(Icon.createWithResource(getContext(), R.drawable.mady)))
+                .build();
+
+    }
+
+    private Slice createNoteSlice(Uri sliceUri) {
+        // TODO: Remote input.
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri);
+        return lb.setColor(0xfff4b400)
+                .addRow(new ListBuilder.RowBuilder(lb)
+                    .setTitle("Create new note")
+                    .setSubtitle("with this note taking app")
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_create),
+                            getBroadcastIntent(ACTION_TOAST, "create note"))
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_voice),
+                            getBroadcastIntent(ACTION_TOAST, "voice note"))
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_camera),
+                            getIntent("android.media.action.IMAGE_CAPTURE")))
+                .build();
+    }
+
+    private Slice createRideSlice(Uri sliceUri) {
+        final ForegroundColorSpan colorSpan = new ForegroundColorSpan(0xff0F9D58);
+        SpannableString headerSubtitle = new SpannableString("Ride in 4 min");
+        headerSubtitle.setSpan(colorSpan, 8, headerSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
+        SpannableString homeSubtitle = new SpannableString("12 miles | 12 min | $9.00");
+        homeSubtitle.setSpan(colorSpan, 20, homeSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
+        SpannableString workSubtitle = new SpannableString("44 miles | 1 hour 45 min | $31.41");
+        workSubtitle.setSpan(colorSpan, 27, workSubtitle.length(), SPAN_EXCLUSIVE_EXCLUSIVE);
+
+        ListBuilder b = new ListBuilder(getContext(), sliceUri);
+        return b.setColor(0xff0F9D58)
+            .addRow(new ListBuilder.RowBuilder(b)
+                    .setContentIntent(getBroadcastIntent(ACTION_TOAST, "work"))
+                    .setTitle("Work")
+                    .setSubtitle(workSubtitle)
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_work),
+                        getBroadcastIntent(ACTION_TOAST, "work")))
+            .addRow(new ListBuilder.RowBuilder(b)
+                    .setContentIntent(getBroadcastIntent(ACTION_TOAST, "home"))
+                    .setTitle("Home")
+                    .setSubtitle(homeSubtitle)
+                    .addEndItem(Icon.createWithResource(getContext(), R.drawable.ic_home),
+                        getBroadcastIntent(ACTION_TOAST, "home")))
+            .build();
+    }
+
+    private Slice createCustomToggleSlice(Uri sliceUri) {
+        ListBuilder b = new ListBuilder(getContext(), sliceUri);
+        return b.setColor(0xffff4081)
+                .addRow(new ListBuilder.RowBuilder(b)
+                    .setTitle("Custom toggle")
+                    .setSubtitle("It can support two states")
+                    .addToggle(getBroadcastIntent(ACTION_TOAST, "star toggled"),
+                            true /* isChecked */,
+                            Icon.createWithResource(getContext(), R.drawable.toggle_star)))
+                .build();
+    }
+
+    private Slice createTwoCustomToggleSlices(Uri sliceUri) {
+        ListBuilder lb = new ListBuilder(getContext(), sliceUri);
+        return lb.setColor(0xffff4081)
+                .addRow(new ListBuilder.RowBuilder(lb)
+                        .setTitle("2 toggles")
+                        .setSubtitle("each supports two states")
+                        .addToggle(getBroadcastIntent(ACTION_TOAST, "first star toggled"),
+                                true /* isChecked */,
+                                Icon.createWithResource(getContext(), R.drawable.toggle_star))
+                        .addToggle(getBroadcastIntent(ACTION_TOAST, "second star toggled"),
+                                false /* isChecked */,
+                                Icon.createWithResource(getContext(), R.drawable.toggle_star)))
+                .build();
+    }
+
+    private Slice createWifiSlice(Uri sliceUri) {
+        // Get wifi state
+        WifiManager wifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        int wifiState = wifiManager.getWifiState();
+        boolean wifiEnabled = false;
+        String state;
+        switch (wifiState) {
+            case WifiManager.WIFI_STATE_DISABLED:
+            case WifiManager.WIFI_STATE_DISABLING:
+                state = "disconnected";
+                break;
+            case WifiManager.WIFI_STATE_ENABLED:
+            case WifiManager.WIFI_STATE_ENABLING:
+                state = wifiManager.getConnectionInfo().getSSID();
+                wifiEnabled = true;
+                break;
+            case WifiManager.WIFI_STATE_UNKNOWN:
+            default:
+                state = ""; // just don't show anything?
+                break;
+        }
+        boolean finalWifiEnabled = wifiEnabled;
+        ListBuilder b = new ListBuilder(getContext(), sliceUri);
+        return b.setColor(0xff4285f4)
+                .addRow(new ListBuilder.RowBuilder(b)
+                    .setTitle("Wi-fi")
+                    .setTitleItem(Icon.createWithResource(getContext(), R.drawable.ic_wifi))
+                    .setSubtitle(state)
+                    .addToggle(getBroadcastIntent(ACTION_WIFI_CHANGED, null), finalWifiEnabled)
+                    .setContentIntent(getIntent(Settings.ACTION_WIFI_SETTINGS)))
+            .build();
+    }
+
+    private PendingIntent getIntent(String action) {
+        Intent intent = new Intent(action);
+        intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
+        PendingIntent pi = PendingIntent.getActivity(getContext(), 0, intent, 0);
+        return pi;
+    }
+
+    private PendingIntent getBroadcastIntent(String action, String message) {
+        Intent intent = new Intent(action);
+        intent.setClassName(getContext().getPackageName(), SliceRenderActivity.class.getName());
+        // Ensure a new PendingIntent is created for each message.
+        int requestCode = 0;
+        if (message != null) {
+            intent.putExtra(EXTRA_TOAST_MESSAGE, message);
+            requestCode = message.hashCode();
+        }
+        return PendingIntent.getBroadcast(getContext(), requestCode, intent,
+                PendingIntent.FLAG_UPDATE_CURRENT);
+    }
+}
diff --git a/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderActivity.java b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderActivity.java
new file mode 100644
index 0000000..debb280
--- /dev/null
+++ b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderActivity.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.app.slice.render;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+
+public class SliceRenderActivity extends Activity {
+    public static final String ACTION_RENDER_DONE = "androidx.app.slice.render.RENDER_DONE";
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        new SliceRenderer(this).renderAll(new Runnable() {
+
+            @Override
+            public void run() {
+                sendBroadcast(new Intent(ACTION_RENDER_DONE));
+                finish();
+            }
+        });
+    }
+}
diff --git a/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderer.java b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderer.java
new file mode 100644
index 0000000..d2b8619
--- /dev/null
+++ b/slices/view/src/androidTest/java/androidx/app/slice/render/SliceRenderer.java
@@ -0,0 +1,222 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.app.slice.render;
+
+import static android.view.View.MeasureSpec.makeMeasureSpec;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.os.AsyncTask;
+import android.os.Environment;
+import android.os.Handler;
+import android.support.v7.widget.RecyclerView;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.util.concurrent.CountDownLatch;
+
+import androidx.app.slice.Slice;
+import androidx.app.slice.SliceProvider;
+import androidx.app.slice.view.test.R;
+import androidx.app.slice.widget.SliceLiveData;
+import androidx.app.slice.widget.SliceView;
+
+public class SliceRenderer {
+
+    private static final String TAG = "SliceRenderer";
+    private static final String SCREENSHOT_DIR = "slice-screenshots";
+    private static File sScreenshotDirectory;
+
+    private final Activity mContext;
+    private final View mLayout;
+    private final SliceView mSV1;
+    private final SliceView mSV2;
+    private final SliceView mSV3;
+    private final ViewGroup mParent;
+    private final Handler mHandler;
+    private final SliceCreator mSliceCreator;
+    private CountDownLatch mDoneLatch;
+
+    public SliceRenderer(Activity context) {
+        mContext = context;
+        mParent = new ViewGroup(mContext) {
+            @Override
+            protected void onLayout(boolean changed, int l, int t, int r, int b) {
+                int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 900,
+                        mContext.getResources().getDisplayMetrics());
+                int height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200,
+                        mContext.getResources().getDisplayMetrics());
+                mLayout.measure(makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
+                        makeMeasureSpec(height, View.MeasureSpec.EXACTLY));
+                mLayout.layout(0, 0, width, height);
+            }
+
+            @Override
+            protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+                return false;
+            }
+        };
+        mLayout = LayoutInflater.from(context).inflate(R.layout.render_layout, null);
+        mSV1 = mLayout.findViewById(R.id.sv1);
+        mSV1.setMode(SliceView.MODE_SHORTCUT);
+        mSV2 = mLayout.findViewById(R.id.sv2);
+        mSV2.setMode(SliceView.MODE_SMALL);
+        mSV3 = mLayout.findViewById(R.id.sv3);
+        mSV3.setMode(SliceView.MODE_LARGE);
+        disableAnims(mLayout);
+        mHandler = new Handler();
+        ((ViewGroup) mContext.getWindow().getDecorView()).addView(mParent);
+        mParent.addView(mLayout);
+        SliceProvider.setSpecs(SliceLiveData.SUPPORTED_SPECS);
+        mSliceCreator = new SliceCreator(mContext);
+    }
+
+    private void disableAnims(View view) {
+        if (view instanceof RecyclerView) {
+            ((RecyclerView) view).setItemAnimator(null);
+        }
+        if (view instanceof ViewGroup) {
+            ViewGroup viewGroup = (ViewGroup) view;
+            for (int i = 0; i < viewGroup.getChildCount(); i++) {
+                disableAnims(viewGroup.getChildAt(i));
+            }
+        }
+    }
+
+
+    private static File getScreenshotDirectory() {
+        if (sScreenshotDirectory == null) {
+            File storage = Environment.getExternalStorageDirectory();
+            sScreenshotDirectory = new File(storage, SCREENSHOT_DIR);
+            if (!sScreenshotDirectory.exists()) {
+                if (!sScreenshotDirectory.mkdirs()) {
+                    throw new RuntimeException(
+                            "Failed to create a screenshot directory.");
+                }
+            }
+        }
+        return sScreenshotDirectory;
+    }
+
+
+    private void doRender() {
+        File output = getScreenshotDirectory();
+        if (!output.exists()) {
+            output.mkdir();
+        }
+        mDoneLatch = new CountDownLatch(SliceCreator.URI_PATHS.length);
+        for (String slice : SliceCreator.URI_PATHS) {
+            doRender(slice, new File(output, String.format("%s.png", slice)));
+        }
+        Log.d(TAG, "Wrote render to " + output.getAbsolutePath());
+        mContext.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                ((ViewGroup) mParent.getParent()).removeView(mParent);
+            }
+        });
+        try {
+            mDoneLatch.await();
+        } catch (InterruptedException e) {
+        }
+    }
+
+    private void doRender(final String slice, final File file) {
+        Log.d(TAG, "Rendering " + slice + " to " + file.getAbsolutePath());
+
+        final Slice s = mSliceCreator.onBindSlice(SliceCreator.getUri(slice, mContext));
+
+        final CountDownLatch l = new CountDownLatch(1);
+        mContext.runOnUiThread(new Runnable() {
+            @Override
+            public void run() {
+                mSV1.setSlice(s);
+                mSV2.setSlice(s);
+                mSV3.setSlice(s);
+                mSV1.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+                    @Override
+                    public void onLayoutChange(View v, int left, int top, int right, int bottom,
+                            int oldLeft, int oldTop, int oldRight, int oldBottom) {
+                        mSV1.removeOnLayoutChangeListener(this);
+                        mSV1.postDelayed(new Runnable() {
+                            @Override
+                            public void run() {
+                                Log.d(TAG, "Drawing " + slice);
+                                Bitmap b = Bitmap.createBitmap(mLayout.getMeasuredWidth(),
+                                        mLayout.getMeasuredHeight(),
+                                        Bitmap.Config.ARGB_8888);
+
+                                mLayout.draw(new Canvas(b));
+                                try {
+                                    doCompress(slice, b, new FileOutputStream(file));
+                                } catch (FileNotFoundException e) {
+                                    throw new RuntimeException(e);
+                                }
+                                l.countDown();
+                            }
+                        }, 10);
+                    }
+                });
+            }
+        });
+        try {
+            l.await();
+        } catch (InterruptedException e) {
+        }
+    }
+
+    private void doCompress(final String slice, final Bitmap b, final FileOutputStream s) {
+        AsyncTask.execute(new Runnable() {
+            @Override
+            public void run() {
+                Log.d(TAG, "Compressing " + slice);
+                if (!b.compress(Bitmap.CompressFormat.PNG, 100, s)) {
+                    throw new RuntimeException("Unable to compress");
+                }
+
+                b.recycle();
+                Log.d(TAG, "Done " + slice);
+                mDoneLatch.countDown();
+            }
+        });
+    }
+
+    public void renderAll(final Runnable runnable) {
+        final ProgressDialog dialog = ProgressDialog.show(mContext, null, "Rendering...");
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+                doRender();
+                mContext.runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        dialog.dismiss();
+                        runnable.run();
+                    }
+                });
+            }
+        }).start();
+    }
+}
diff --git a/slices/view/src/androidTest/res/drawable/ic_call.xml b/slices/view/src/androidTest/res/drawable/ic_call.xml
new file mode 100644
index 0000000..ebf9de6
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_call.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_camera.xml b/slices/view/src/androidTest/res/drawable/ic_camera.xml
new file mode 100644
index 0000000..74b6bf9
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_camera.xml
@@ -0,0 +1,32 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M 12 8.8 C 13.7673111995 8.8 15.2 10.2326888005 15.2 12 C 15.2 13.7673111995 13.7673111995 15.2 12 15.2 C 10.2326888005 15.2 8.8 13.7673111995 8.8 12 C 8.8 10.2326888005 10.2326888005 8.8 12 8.8 Z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M9 2L7.17 4H4c-1.1 0-2 .9-2 2v12c0 1.1 .9 2 2 2h16c1.1 0 2-.9
+2-2V6c0-1.1-.9-2-2-2h-3.17L15 2H9zm3 15c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5
+5-2.24 5-5 5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/drawable/ic_car.xml b/slices/view/src/androidTest/res/drawable/ic_car.xml
new file mode 100644
index 0000000..6bab660
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_car.xml
@@ -0,0 +1,31 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M18.92 6.01C18.72 5.42 18.16 5 17.5 5h-11c-.66 0-1.21 .42 -1.42 1.01L3 12v8c0
+.55 .45 1 1 1h1c.55 0 1-.45 1-1v-1h12v1c0 .55 .45 1 1 1h1c.55 0 1-.45
+1-1v-8l-2.08-5.99zM6.5 16c-.83 0-1.5-.67-1.5-1.5S5.67 13 6.5 13s1.5 .67 1.5
+1.5S7.33 16 6.5 16zm11 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5 .67 1.5
+1.5-.67 1.5-1.5 1.5zM5 11l1.5-4.5h11L19 11H5z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/drawable/ic_create.xml b/slices/view/src/androidTest/res/drawable/ic_create.xml
new file mode 100644
index 0000000..d1666a8
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_create.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_email.xml b/slices/view/src/androidTest/res/drawable/ic_email.xml
new file mode 100644
index 0000000..ce97ab8
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_email.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_home.xml b/slices/view/src/androidTest/res/drawable/ic_home.xml
new file mode 100644
index 0000000..b278230
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_home.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:fillColor="#000000"
+        android:pathData="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" />
+    <path
+        android:pathData="M0 0h24v24H0z" />
+</vector>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/drawable/ic_large.xml b/slices/view/src/androidTest/res/drawable/ic_large.xml
new file mode 100644
index 0000000..79ac590
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_large.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48.0dp"
+        android:height="48.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M34.0,2.02L14.0,2.0c-2.21,0.0 -4.0,1.79 -4.0,4.0l0.0,36.0c0.0,2.21 1.79,4.0 4.0,4.0l20.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L38.0,6.0c0.0,-2.21 -1.79,-3.98 -4.0,-3.98zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0l0.0,28.0z"/>
+    <path
+        android:strokeColor="#FF000000"
+        android:strokeWidth="2"
+        android:pathData="M16,18 l16,0 l0,10 l-16,0z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_shortcut.xml b/slices/view/src/androidTest/res/drawable/ic_shortcut.xml
new file mode 100644
index 0000000..bf9572a
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_shortcut.xml
@@ -0,0 +1,34 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:height="48.0dp"
+    android:viewportHeight="48.0"
+    android:viewportWidth="48.0"
+    android:width="48.0dp">
+    <path
+        android:fillColor="#e2e2e2"
+        android:pathData="M24.0,24.0m-19.0,0.0a19.0,19.0 0.0,1.0 1.0,38.0 0.0a19.0,19.0 0.0,1.0 1.0,-38.0 0.0"/>
+    <group
+        android:scaleX=".7"
+        android:scaleY=".7"
+        android:translateX="7.2"
+        android:translateY="7.2">
+
+        <path
+            android:fillColor="#ff000000"
+            android:pathData="M12.0,36.0c0.0,1.0 0.9,2.0 2.0,2.0l2.0,0.0l0.0,7.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0l0.0,-7.0l4.0,0.0l0.0,7.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0l0.0,-7.0l2.0,0.0c1.1,0.0 2.0,-0.9 2.0,-2.0L36.0,16.0L12.0,16.0l0.0,20.0zM7.0,16.0c-1.66,0.0 -3.0,1.34 -3.0,3.0l0.0,14.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0L10.0,19.0c0.0,-1.66 -1.34,-3.0 -3.0,-3.0zm34.0,0.0c-1.66,0.0 -3.0,1.34 -3.0,3.0l0.0,14.0c0.0,1.66 1.34,3.0 3.0,3.0s3.0,-1.34 3.0,-3.0L44.0,19.0c0.0,-1.66 -1.34,-3.0 -3.0,-3.0zM31.06,4.32l2.61,-2.61c0.39,-0.3 0.39,-1.02 0.0,-1.41 -0.39,-0.39 -1.02,-0.39 -1.41,0.0L29.3,3.25C27.7,2.46 25.91,2.0 24.0,2.0c-1.92,0.0 -3.7,0.46 -5.33,1.26L15.0,0.29c-0.39,-0.39 -1.02,-0.39 -1.41,0.0 -0.3,0.39 -0.39,1.02 0.0,1.41l2.62,2.62C13.94,6.51 12.0,10.03 12.0,14.0l24.0,0.0c0.0,-3.98 -1.95,-7.5 -4.94,-9.68zM20.0,10.0l-2.0,0.0L18.0,8.0l2.0,0.0l0.0,2.0zm10.0,0.0l-2.0,0.0L28.0,8.0l2.0,0.0l0.0,2.0z"/>
+    </group>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_small.xml b/slices/view/src/androidTest/res/drawable/ic_small.xml
new file mode 100644
index 0000000..8fd43df
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_small.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="48.0dp"
+        android:height="48.0dp"
+        android:viewportWidth="48.0"
+        android:viewportHeight="48.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M34.0,2.02L14.0,2.0c-2.21,0.0 -4.0,1.79 -4.0,4.0l0.0,36.0c0.0,2.21 1.79,4.0 4.0,4.0l20.0,0.0c2.21,0.0 4.0,-1.79 4.0,-4.0L38.0,6.0c0.0,-2.21 -1.79,-3.98 -4.0,-3.98zM34.0,38.0L14.0,38.0L14.0,10.0l20.0,0.0l0.0,28.0z"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M16,18 l16,0 l0,4 l-16,0z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_star_off.xml b/slices/view/src/androidTest/res/drawable/ic_star_off.xml
new file mode 100644
index 0000000..7f023b3
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_star_off.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_star_on.xml b/slices/view/src/androidTest/res/drawable/ic_star_on.xml
new file mode 100644
index 0000000..f3ca086
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_star_on.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_text.xml b/slices/view/src/androidTest/res/drawable/ic_text.xml
new file mode 100644
index 0000000..d2876bf
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_text.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_video.xml b/slices/view/src/androidTest/res/drawable/ic_video.xml
new file mode 100644
index 0000000..e23eac8
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_video.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_voice.xml b/slices/view/src/androidTest/res/drawable/ic_voice.xml
new file mode 100644
index 0000000..8f465bb
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_voice.xml
@@ -0,0 +1,24 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12,15c1.66,0 2.99,-1.34 2.99,-3L15,6c0,-1.66 -1.34,-3 -3,-3S9,4.34 9,6v6c0,1.66 1.34,3 3,3zM17.3,12c0,3 -2.54,5.1 -5.3,5.1S6.7,15 6.7,12L5,12c0,3.42 2.72,6.23 6,6.72L11,22h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_wifi.xml b/slices/view/src/androidTest/res/drawable/ic_wifi.xml
new file mode 100644
index 0000000..4fbfd60
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_wifi.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0">
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M12.01,21.49L23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4C5.28,3 0.81,6.66 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01z"
+        android:fillAlpha=".3"/>
+    <path
+        android:fillColor="#FF000000"
+        android:pathData="M3.53,10.95l8.46,10.54 0.01,0.01 0.01,-0.01 8.46,-10.54C20.04,10.62 16.81,8 12,8c-4.81,0 -8.04,2.62 -8.47,2.95z"/>
+</vector>
diff --git a/slices/view/src/androidTest/res/drawable/ic_work.xml b/slices/view/src/androidTest/res/drawable/ic_work.xml
new file mode 100644
index 0000000..6806402
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/ic_work.xml
@@ -0,0 +1,28 @@
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="24dp"
+    android:height="24dp"
+    android:viewportWidth="24"
+    android:viewportHeight="24">
+
+    <path
+        android:pathData="M0 0h24v24H0z" />
+    <path
+        android:fillColor="#000000"
+        android:pathData="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99 .89 -1.99 2L2
+19c0 1.11 .89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z" />
+</vector>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/drawable/mady.jpg b/slices/view/src/androidTest/res/drawable/mady.jpg
new file mode 100644
index 0000000..8b61f1b
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/mady.jpg
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/slices_1.jpg b/slices/view/src/androidTest/res/drawable/slices_1.jpg
new file mode 100644
index 0000000..31cc065
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/slices_1.jpg
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/slices_2.jpg b/slices/view/src/androidTest/res/drawable/slices_2.jpg
new file mode 100644
index 0000000..adbe1d3
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/slices_2.jpg
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/slices_3.jpg b/slices/view/src/androidTest/res/drawable/slices_3.jpg
new file mode 100644
index 0000000..6617019
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/slices_3.jpg
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/slices_4.jpg b/slices/view/src/androidTest/res/drawable/slices_4.jpg
new file mode 100644
index 0000000..d00a8b3
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/slices_4.jpg
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/toggle_star.xml b/slices/view/src/androidTest/res/drawable/toggle_star.xml
new file mode 100644
index 0000000..e964925
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/toggle_star.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item
+        android:id="@+id/on"
+        android:state_checked="true"
+        android:drawable="@drawable/ic_star_on" />
+    <item
+        android:id="@+id/off"
+        android:drawable="@drawable/ic_star_off" />
+</selector>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/drawable/weather_1.png b/slices/view/src/androidTest/res/drawable/weather_1.png
new file mode 100644
index 0000000..7c4034e
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/weather_1.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/weather_2.png b/slices/view/src/androidTest/res/drawable/weather_2.png
new file mode 100644
index 0000000..f1b6672
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/weather_2.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/weather_3.png b/slices/view/src/androidTest/res/drawable/weather_3.png
new file mode 100644
index 0000000..a5db683
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/weather_3.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/drawable/weather_4.png b/slices/view/src/androidTest/res/drawable/weather_4.png
new file mode 100644
index 0000000..0b7f3b0
--- /dev/null
+++ b/slices/view/src/androidTest/res/drawable/weather_4.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/layout/activity_demo.xml b/slices/view/src/androidTest/res/layout/activity_demo.xml
new file mode 100644
index 0000000..f557f40
--- /dev/null
+++ b/slices/view/src/androidTest/res/layout/activity_demo.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    tools:context="com.example.android.support.content.demos.ContentPagerDemoActivity">
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/toolbar"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/actionBarSize"
+        android:background="?attr/colorPrimary"
+        android:theme="@style/AppTheme.AppBarOverlay"
+        app:popupTheme="@style/AppTheme.PopupOverlay"/>
+
+    <android.support.v7.widget.RecyclerView
+        android:id="@+id/list"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+</LinearLayout>
diff --git a/slices/view/src/androidTest/res/layout/render_layout.xml b/slices/view/src/androidTest/res/layout/render_layout.xml
new file mode 100644
index 0000000..a8ed779
--- /dev/null
+++ b/slices/view/src/androidTest/res/layout/render_layout.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:layout_width="1200dp"
+              android:layout_height="400dp"
+              android:background="@android:color/black"
+              android:orientation="horizontal"
+              android:padding="20dp">
+
+
+    <FrameLayout android:layout_width="wrap_content"
+                 android:layout_height="wrap_content">
+        <androidx.app.slice.widget.SliceView
+            android:id="@+id/sv1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"/>
+    </FrameLayout>
+
+    <FrameLayout android:layout_width="400dp"
+                 android:layout_height="wrap_content"
+                 android:layout_marginStart="20dp">
+        <androidx.app.slice.widget.SliceView
+            android:id="@+id/sv2"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@android:color/white"
+            android:paddingEnd="16dp"
+            android:paddingStart="16dp"/>
+    </FrameLayout>
+
+    <FrameLayout android:layout_width="400dp"
+                 android:layout_height="wrap_content"
+                 android:layout_marginStart="20dp">
+        <androidx.app.slice.widget.SliceView
+            android:id="@+id/sv3"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@android:color/white"
+            android:paddingEnd="16dp"
+            android:paddingStart="16dp"/>
+    </FrameLayout>
+
+
+</LinearLayout>
\ No newline at end of file
diff --git a/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher.png b/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher_round.png b/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9a078e3
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher.png b/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher_round.png b/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 0000000..efc028a
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher.png b/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher_round.png b/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..3af2608
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher.png b/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher_round.png b/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..9bec2e6
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher.png b/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher_round.png b/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 0000000..34947cd
--- /dev/null
+++ b/slices/view/src/androidTest/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/slices/view/src/androidTest/res/values/colors.xml b/slices/view/src/androidTest/res/values/colors.xml
new file mode 100644
index 0000000..86b4304
--- /dev/null
+++ b/slices/view/src/androidTest/res/values/colors.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <color name="colorPrimary">#3F51B5</color>
+    <color name="colorPrimaryDark">#303F9F</color>
+    <color name="colorAccent">#FF4081</color>
+</resources>
diff --git a/slices/view/src/androidTest/res/values/strings.xml b/slices/view/src/androidTest/res/values/strings.xml
new file mode 100644
index 0000000..89583bd
--- /dev/null
+++ b/slices/view/src/androidTest/res/values/strings.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="app_name">Slice Browser</string>
+</resources>
diff --git a/slices/view/src/androidTest/res/values/styles.xml b/slices/view/src/androidTest/res/values/styles.xml
new file mode 100644
index 0000000..afb6bad
--- /dev/null
+++ b/slices/view/src/androidTest/res/values/styles.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+        <!-- Customize your theme here. -->
+        <item name="colorPrimary">@color/colorPrimary</item>
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
+        <item name="colorAccent">@color/colorAccent</item>
+    </style>
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
+
+</resources>
diff --git a/slices/view/src/main/java/androidx/app/slice/SliceUtils.java b/slices/view/src/main/java/androidx/app/slice/SliceUtils.java
index ededbfd..6a6ab14 100644
--- a/slices/view/src/main/java/androidx/app/slice/SliceUtils.java
+++ b/slices/view/src/main/java/androidx/app/slice/SliceUtils.java
@@ -16,6 +16,7 @@
 
 package androidx.app.slice;
 
+import static android.app.slice.Slice.HINT_PARTIAL;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_REMOTE_INPUT;
@@ -29,6 +30,8 @@
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import androidx.app.slice.core.SliceQuery;
+
 /**
  * Utilities for dealing with slices.
  */
@@ -155,4 +158,49 @@
             return this;
         }
     }
+
+    /**
+     * @hide
+     */
+    @RestrictTo(RestrictTo.Scope.LIBRARY)
+    @IntDef({
+            LOADING_ALL, LOADING_PARTIAL, LOADING_COMPLETE
+    })
+    public @interface SliceLoadingState{}
+
+    /**
+     * Indicates this slice is empty and waiting for content to be loaded.
+     */
+    public static final int LOADING_ALL = 0;
+    /**
+     * Indicates this slice has some content but is waiting for other content to be loaded.
+     */
+    public static final int LOADING_PARTIAL = 1;
+    /**
+     * Indicates this slice has fully loaded and is not waiting for other content.
+     */
+    public static final int LOADING_COMPLETE = 2;
+
+    /**
+     * @return the current loading state of the provided {@link Slice}.
+     *
+     * @see #LOADING_ALL
+     * @see #LOADING_PARTIAL
+     * @see #LOADING_COMPLETE
+     */
+    public static int getLoadingState(Slice slice) {
+        // Check loading state
+        boolean hasHintPartial =
+                SliceQuery.find(slice, null, HINT_PARTIAL, null) != null;
+        if (hasHintPartial && slice.getItems().size() == 0) {
+            // Empty slice
+            return LOADING_ALL;
+        } else if (hasHintPartial) {
+            // Slice with specific content to load
+            return LOADING_PARTIAL;
+        } else {
+            // Full slice
+            return LOADING_COMPLETE;
+        }
+    }
 }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
index 8d01c64..a5959d3 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/GridRowView.java
@@ -288,6 +288,8 @@
             boolean title = SliceQuery.hasAnyHints(item, HINT_LARGE, HINT_TITLE);
             TextView tv = (TextView) LayoutInflater.from(getContext()).inflate(title
                     ? TITLE_TEXT_LAYOUT : TEXT_LAYOUT, null);
+            tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, title ? mTitleSize : mSubtitleSize);
+            tv.setTextColor(title ? mTitleColor : mSubtitleColor);
             CharSequence text = FORMAT_TIMESTAMP.equals(format)
                     ? SliceViewUtil.getRelativeTimeString(item.getTimestamp())
                     : item.getText();
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
index fa8397e..d015893 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/LargeTemplateView.java
@@ -54,6 +54,12 @@
     }
 
     @Override
+    public void setTint(int tint) {
+        super.setTint(tint);
+        populate();
+    }
+
+    @Override
     public @SliceView.SliceMode int getMode() {
         return SliceView.MODE_LARGE;
     }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java b/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
index 86e9409..5b29ab2 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/ListContent.java
@@ -19,6 +19,7 @@
 import static android.app.slice.Slice.HINT_ACTIONS;
 import static android.app.slice.Slice.HINT_LIST;
 import static android.app.slice.Slice.HINT_LIST_ITEM;
+import static android.app.slice.Slice.HINT_SUMMARY;
 import static android.app.slice.Slice.SUBTYPE_COLOR;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_INT;
@@ -33,7 +34,6 @@
 
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceItem;
-import androidx.app.slice.core.SliceHints;
 import androidx.app.slice.core.SliceQuery;
 
 /**
@@ -76,7 +76,7 @@
         for (int i = 0; i < children.size(); i++) {
             final SliceItem child = children.get(i);
             final String format = child.getFormat();
-            if (!child.hasAnyHints(SliceHints.HINT_SUMMARY, HINT_ACTIONS)
+            if (!child.hasAnyHints(HINT_SUMMARY, HINT_ACTIONS)
                     && (FORMAT_ACTION.equals(format) || FORMAT_SLICE.equals(format))) {
                 if (!mHasHeader && !child.hasHint(HINT_LIST_ITEM)) {
                     mHasHeader = true;
@@ -125,7 +125,7 @@
     private static SliceItem getSummaryItem(@NonNull Slice slice) {
         List<SliceItem> items = slice.getItems();
         // See if a summary is specified
-        SliceItem summary = SliceQuery.find(slice, FORMAT_SLICE, SliceHints.HINT_SUMMARY, null);
+        SliceItem summary = SliceQuery.find(slice, FORMAT_SLICE, HINT_SUMMARY, null);
         if (summary != null) {
             return summary;
         }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
index 0e730a5..352983d 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
@@ -34,7 +34,6 @@
 import java.util.List;
 
 import androidx.app.slice.SliceItem;
-import androidx.app.slice.core.SliceHints;
 import androidx.app.slice.core.SliceQuery;
 
 /**
@@ -238,8 +237,7 @@
      */
     private static boolean isStartType(SliceItem item) {
         final String type = item.getFormat();
-        return (!item.hasHint(SliceHints.SUBTYPE_TOGGLE)
-                && (FORMAT_ACTION.equals(type) && (SliceQuery.find(item, FORMAT_IMAGE) != null)))
+        return (FORMAT_ACTION.equals(type) && (SliceQuery.find(item, FORMAT_IMAGE) != null))
                 || FORMAT_IMAGE.equals(type)
                 || FORMAT_TIMESTAMP.equals(type);
     }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/RowView.java b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
index 47a8045..a81c55b 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/RowView.java
@@ -16,18 +16,18 @@
 
 package androidx.app.slice.widget;
 
+import static android.app.slice.Slice.EXTRA_TOGGLE_STATE;
 import static android.app.slice.Slice.HINT_NO_TINT;
 import static android.app.slice.Slice.HINT_SELECTED;
+import static android.app.slice.Slice.SUBTYPE_TOGGLE;
 import static android.app.slice.SliceItem.FORMAT_ACTION;
 import static android.app.slice.SliceItem.FORMAT_IMAGE;
 import static android.app.slice.SliceItem.FORMAT_INT;
 import static android.app.slice.SliceItem.FORMAT_TIMESTAMP;
 
 import static androidx.app.slice.core.SliceHints.EXTRA_SLIDER_VALUE;
-import static androidx.app.slice.core.SliceHints.EXTRA_TOGGLE_STATE;
 import static androidx.app.slice.core.SliceHints.SUBTYPE_MAX;
 import static androidx.app.slice.core.SliceHints.SUBTYPE_PROGRESS;
-import static androidx.app.slice.core.SliceHints.SUBTYPE_TOGGLE;
 import static androidx.app.slice.widget.SliceView.MODE_LARGE;
 import static androidx.app.slice.widget.SliceView.MODE_SMALL;
 
@@ -40,6 +40,7 @@
 import android.support.annotation.ColorInt;
 import android.support.annotation.RestrictTo;
 import android.util.Log;
+import android.util.TypedValue;
 import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CompoundButton;
@@ -168,12 +169,20 @@
         if (titleItem != null) {
             mPrimaryText.setText(titleItem.getText());
         }
+        mPrimaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIsHeader
+                ? mHeaderTitleSize
+                : mTitleSize);
+        mPrimaryText.setTextColor(mTitleColor);
         mPrimaryText.setVisibility(titleItem != null ? View.VISIBLE : View.GONE);
 
         final SliceItem subTitle = mRowContent.getSubtitleItem();
         if (subTitle != null) {
             mSecondaryText.setText(subTitle.getText());
         }
+        mSecondaryText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mIsHeader
+                ? mHeaderSubtitleSize
+                : mSubtitleSize);
+        mSecondaryText.setTextColor(mSubtitleColor);
         mSecondaryText.setVisibility(subTitle != null ? View.VISIBLE : View.GONE);
 
         final SliceItem slider = mRowContent.getSlider();
@@ -343,7 +352,7 @@
         SliceItem timeStamp = null;
         ViewGroup container = isStart ? mStartContainer : mEndContainer;
         if (FORMAT_ACTION.equals(sliceItem.getFormat())) {
-            if (SliceQuery.hasHints(sliceItem.getSlice(), SUBTYPE_TOGGLE)) {
+            if (SUBTYPE_TOGGLE.equals(sliceItem.getSubType())) {
                 addToggle(sliceItem, color, container);
                 return true;
             }
@@ -371,6 +380,8 @@
         } else if (timeStamp != null) {
             TextView tv = new TextView(getContext());
             tv.setText(SliceViewUtil.getRelativeTimeString(sliceItem.getTimestamp()));
+            tv.setTextSize(TypedValue.COMPLEX_UNIT_PX, mSubtitleSize);
+            tv.setTextColor(mSubtitleColor);
             container.addView(tv);
             addedView = tv;
         }
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceChildView.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceChildView.java
index f731c96..a9faf86 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SliceChildView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceChildView.java
@@ -17,6 +17,7 @@
 package androidx.app.slice.widget;
 
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.support.annotation.ColorInt;
 import android.support.annotation.NonNull;
 import android.support.annotation.RestrictTo;
@@ -26,6 +27,7 @@
 
 import androidx.app.slice.Slice;
 import androidx.app.slice.SliceItem;
+import androidx.app.slice.view.R;
 
 /**
  * Base class for children views of {@link SliceView}.
@@ -35,7 +37,15 @@
 public abstract class SliceChildView extends FrameLayout {
 
     protected SliceView.OnSliceActionListener mObserver;
-    protected int mTintColor;
+    protected int mTintColor = -1;
+    protected int mTitleColor;
+    protected int mSubtitleColor;
+    protected int mHeaderTitleSize;
+    protected int mHeaderSubtitleSize;
+    protected int mTitleSize;
+    protected int mSubtitleSize;
+    protected int mGridTitleSize;
+    protected int mGridSubtitleSize;
 
     public SliceChildView(@NonNull Context context) {
         super(context);
@@ -85,7 +95,26 @@
      * Populates style information for this view.
      */
     public void setStyle(AttributeSet attrs) {
-        // TODO
+        TypedArray a = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
+                R.attr.sliceViewStyle, R.style.Widget_SliceView);
+        try {
+            int themeColor = a.getColor(R.styleable.SliceView_tintColor, -1);
+            mTintColor = themeColor != -1 ? themeColor : mTintColor;
+            mTitleColor = a.getColor(R.styleable.SliceView_titleColor, 0);
+            mSubtitleColor = a.getColor(R.styleable.SliceView_subtitleColor, 0);
+            mHeaderTitleSize = (int) a.getDimension(
+                    R.styleable.SliceView_headerTitleSize, 0);
+            mHeaderSubtitleSize = (int) a.getDimension(
+                    R.styleable.SliceView_headerSubtitleSize, 0);
+            mTitleSize = (int) a.getDimension(R.styleable.SliceView_titleSize, 0);
+            mSubtitleSize = (int) a.getDimension(
+                    R.styleable.SliceView_subtitleSize, 0);
+            mGridTitleSize = (int) a.getDimension(R.styleable.SliceView_gridTitleSize, 0);
+            mGridSubtitleSize = (int) a.getDimension(
+                    R.styleable.SliceView_gridSubtitleSize, 0);
+        } finally {
+            a.recycle();
+        }
     }
 
     /**
diff --git a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
index dbf1d67..b974814 100644
--- a/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
+++ b/slices/view/src/main/java/androidx/app/slice/widget/SliceView.java
@@ -24,6 +24,7 @@
 
 import android.arch.lifecycle.Observer;
 import android.content.Context;
+import android.content.res.TypedArray;
 import android.graphics.drawable.ColorDrawable;
 import android.support.annotation.IntDef;
 import android.support.annotation.NonNull;
@@ -119,6 +120,7 @@
      * that selection.
      */
     private static final int MODE_AUTO = 0;
+
     private int mMode = MODE_AUTO;
     private SliceChildView mCurrentView;
     private Slice mCurrentSlice;
@@ -129,24 +131,31 @@
     private boolean mShowActions = true;
     private boolean mIsScrollable = true;
 
-    private int mThemeTintColor = -1;
     private AttributeSet mAttrs;
+    private int mThemeTintColor;
 
     public SliceView(Context context) {
         this(context, null);
     }
 
     public SliceView(Context context, @Nullable AttributeSet attrs) {
-        this(context, attrs, 0);
+        this(context, attrs, R.attr.sliceViewStyle);
     }
 
     public SliceView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
+        this(context, attrs, defStyleAttr, R.style.Widget_SliceView);
     }
 
     public SliceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
         mAttrs = attrs;
+        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SliceView,
+                defStyleAttr, defStyleRes);
+        try {
+            mThemeTintColor = a.getColor(R.styleable.SliceView_tintColor, -1);
+        } finally {
+            a.recycle();
+        }
         mActions = new ActionRow(getContext(), true);
         mActions.setBackground(new ColorDrawable(0xffeeeeee));
         mCurrentView = new LargeTemplateView(getContext());
@@ -247,6 +256,18 @@
     }
 
     /**
+     * Contents of a slice such as icons, text, and controls (e.g. toggle) can be tinted. Normally
+     * a color for tinting will be provided by the slice. Using this method will override
+     * this color information and instead tint elements with the provided color.
+     *
+     * @param tintColor the color to use for tinting contents of this view.
+     */
+    public void setTint(int tintColor) {
+        mThemeTintColor = tintColor;
+        mCurrentView.setTint(tintColor);
+    }
+
+    /**
      * @hide
      */
     @RestrictTo(RestrictTo.Scope.LIBRARY)
@@ -318,6 +339,7 @@
         }
         // Styles
         mCurrentView.setStyle(mAttrs);
+        mCurrentView.setTint(getTintColor());
         // Set the slice
         SliceItem actionRow = SliceQuery.find(mCurrentSlice, FORMAT_SLICE,
                 HINT_ACTIONS,
diff --git a/slices/view/src/main/res-public/values/public_attrs.xml b/slices/view/src/main/res-public/values/public_attrs.xml
new file mode 100644
index 0000000..ad909ea
--- /dev/null
+++ b/slices/view/src/main/res-public/values/public_attrs.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2017 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Definitions of attributes to be exposed to the public -->
+<resources>
+    <public type="attr" name="titleColor" />
+    <public type="attr" name="subtitleColor" />
+    <public type="attr" name="tintColor" />
+    <public type="attr" name="headerTitleSize" />
+    <public type="attr" name="headerSubtitleSize" />
+    <public type="attr" name="titleSize" />
+    <public type="attr" name="subtitleSize" />
+    <public type="attr" name="gridTitleSize" />
+    <public type="attr" name="gridSubtitleSize" />
+    <public type="attr" name="sliceViewStyle" />
+</resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res-public/values/public_styles.xml b/slices/view/src/main/res-public/values/public_styles.xml
new file mode 100644
index 0000000..36f3e26
--- /dev/null
+++ b/slices/view/src/main/res-public/values/public_styles.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<!-- Definitions of styles to be exposed to the public -->
+<resources>
+    <public type="style" name="Widget.SliceView"/>
+</resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res/layout/abc_slice_small_template.xml b/slices/view/src/main/res/layout/abc_slice_small_template.xml
index 706ded6..2d5e913 100644
--- a/slices/view/src/main/res/layout/abc_slice_small_template.xml
+++ b/slices/view/src/main/res/layout/abc_slice_small_template.xml
@@ -43,16 +43,12 @@
         <TextView android:id="@android:id/title"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textAppearance="?android:attr/textAppearanceListItem"
-            android:textColor="?android:attr/textColorPrimary"
             android:maxLines="2"/>
 
         <TextView android:id="@android:id/summary"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignStart="@android:id/title"
-            android:textAppearance="?attr/textAppearanceListItemSecondary"
-            android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10" />
 
         <SeekBar
diff --git a/slices/view/src/main/res/values/attrs.xml b/slices/view/src/main/res/values/attrs.xml
new file mode 100644
index 0000000..5779d31
--- /dev/null
+++ b/slices/view/src/main/res/values/attrs.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <declare-styleable name="SliceView">
+        <!-- Colors -->
+        <attr name="titleColor" format="color" />
+        <attr name="subtitleColor" format="color" />
+        <attr name="tintColor" format="color" />
+
+        <!-- Text sizes -->
+        <attr name="headerTitleSize" format="dimension" />
+        <attr name="headerSubtitleSize" format="dimension"/>
+        <attr name="titleSize" format="dimension" />
+        <attr name="subtitleSize" format="dimension" />
+        <attr name="gridTitleSize" format="dimension" />
+        <attr name="gridSubtitleSize" format="dimension" />
+    </declare-styleable>
+
+    <attr name="sliceViewStyle" format="reference"/>
+</resources>
\ No newline at end of file
diff --git a/slices/view/src/main/res/values/styles.xml b/slices/view/src/main/res/values/styles.xml
new file mode 100644
index 0000000..4b27820
--- /dev/null
+++ b/slices/view/src/main/res/values/styles.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright 2018 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<resources>
+    <style name="Widget.SliceView" parent="">
+        <item name="tintColor">@null</item>
+        <!-- Colors -->
+        <item name="titleColor">?android:attr/textColorPrimary</item>
+        <item name="subtitleColor">?android:attr/textColorSecondary</item>
+
+        <!-- Text sizes -->
+        <item name="headerTitleSize">16sp</item>
+        <item name="headerSubtitleSize">14sp</item>
+        <item name="titleSize">14sp</item>
+        <item name="subtitleSize">14sp</item>
+        <item name="gridTitleSize">12sp</item>
+        <item name="gridSubtitleSize">12sp</item>
+    </style>
+</resources>
\ No newline at end of file
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java b/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
index ddcb9fc..9979363 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/ActivityChooserView.java
@@ -44,6 +44,7 @@
 import android.widget.BaseAdapter;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
@@ -857,7 +858,7 @@
      * @hide
      */
     @RestrictTo(LIBRARY_GROUP)
-    public static class InnerLayout extends LinearLayoutCompat {
+    public static class InnerLayout extends LinearLayout {
 
         private static final int[] TINT_ATTRS = {
                 android.R.attr.background
diff --git a/v7/appcompat/src/main/java/android/support/v7/widget/ScrollingTabContainerView.java b/v7/appcompat/src/main/java/android/support/v7/widget/ScrollingTabContainerView.java
index 345e318..1244508f 100644
--- a/v7/appcompat/src/main/java/android/support/v7/widget/ScrollingTabContainerView.java
+++ b/v7/appcompat/src/main/java/android/support/v7/widget/ScrollingTabContainerView.java
@@ -42,6 +42,7 @@
 import android.widget.BaseAdapter;
 import android.widget.HorizontalScrollView;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.ListView;
 import android.widget.Spinner;
 import android.widget.TextView;
@@ -375,7 +376,7 @@
         // no-op
     }
 
-    private class TabView extends LinearLayoutCompat {
+    private class TabView extends LinearLayout {
         private final int[] BG_ATTRS = {
                 android.R.attr.background
         };
diff --git a/v7/preference/res/layout-v11/preference.xml b/v7/preference/res/layout-v11/preference.xml
deleted file mode 100644
index 981288f..0000000
--- a/v7/preference/res/layout-v11/preference.xml
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2015 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:gravity="center_vertical"
-    android:paddingEnd="?android:attr/scrollbarSize"
-    android:background="?android:attr/selectableItemBackground"
-    android:focusable="true" >
-
-    <FrameLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
-        <android.support.v7.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxWidth="48dp"
-            app:maxHeight="48dp" />
-    </FrameLayout>
-
-    <RelativeLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="15dip"
-        android:layout_marginEnd="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
-
-        <TextView android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textColor="?android:attr/textColorPrimary"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-
-        <TextView android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignStart="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="4" />
-
-    </RelativeLayout>
-
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical"
-        android:orientation="vertical" />
-
-</LinearLayout>
diff --git a/v7/preference/res/layout-v11/preference_dropdown.xml b/v7/preference/res/layout-v11/preference_dropdown.xml
deleted file mode 100644
index 0e5b35dc..0000000
--- a/v7/preference/res/layout-v11/preference_dropdown.xml
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2016 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License
-  -->
-
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="wrap_content"
-    android:minHeight="?android:attr/listPreferredItemHeight"
-    android:gravity="center_vertical"
-    android:paddingEnd="?android:attr/scrollbarSize"
-    android:background="?android:attr/selectableItemBackground"
-    android:focusable="true" >
-
-    <Spinner
-        android:id="@+id/spinner"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:visibility="invisible" />
-
-    <FrameLayout
-        android:id="@+id/icon_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content">
-        <android.support.v7.internal.widget.PreferenceImageView
-            android:id="@android:id/icon"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            app:maxWidth="48dp"
-            app:maxHeight="48dp" />
-    </FrameLayout>
-
-    <RelativeLayout
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="15dip"
-        android:layout_marginEnd="6dip"
-        android:layout_marginTop="6dip"
-        android:layout_marginBottom="6dip"
-        android:layout_weight="1">
-
-        <TextView android:id="@android:id/title"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:singleLine="true"
-            android:textAppearance="?android:attr/textAppearanceLarge"
-            android:textColor="?android:attr/textColorPrimary"
-            android:ellipsize="marquee"
-            android:fadingEdge="horizontal" />
-
-        <TextView android:id="@android:id/summary"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_below="@android:id/title"
-            android:layout_alignStart="@android:id/title"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="?android:attr/textColorSecondary"
-            android:maxLines="4" />
-
-    </RelativeLayout>
-
-    <!-- Preference should place its actual preference widget here. -->
-    <LinearLayout android:id="@android:id/widget_frame"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:gravity="center_vertical"
-        android:orientation="vertical" />
-
-</LinearLayout>
diff --git a/v7/preference/res/layout-v7/expand_button.xml b/v7/preference/res/layout/expand_button.xml
similarity index 100%
rename from v7/preference/res/layout-v7/expand_button.xml
rename to v7/preference/res/layout/expand_button.xml
diff --git a/v7/preference/res/layout/preference.xml b/v7/preference/res/layout/preference.xml
index f75bc68..981288f 100644
--- a/v7/preference/res/layout/preference.xml
+++ b/v7/preference/res/layout/preference.xml
@@ -16,24 +16,25 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:gravity="center_vertical"
     android:paddingEnd="?android:attr/scrollbarSize"
-    android:background="@android:drawable/list_selector_background"
+    android:background="?android:attr/selectableItemBackground"
     android:focusable="true" >
 
     <FrameLayout
         android:id="@+id/icon_frame"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content">
-        <ImageView
+        <android.support.v7.internal.widget.PreferenceImageView
             android:id="@android:id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
-            />
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
     </FrameLayout>
 
     <RelativeLayout
diff --git a/v7/preference/res/layout/preference_dropdown.xml b/v7/preference/res/layout/preference_dropdown.xml
index 1c51e09..0e5b35dc 100644
--- a/v7/preference/res/layout/preference_dropdown.xml
+++ b/v7/preference/res/layout/preference_dropdown.xml
@@ -16,12 +16,13 @@
   -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:gravity="center_vertical"
     android:paddingEnd="?android:attr/scrollbarSize"
-    android:background="@android:drawable/list_selector_background"
+    android:background="?android:attr/selectableItemBackground"
     android:focusable="true" >
 
     <Spinner
@@ -34,12 +35,12 @@
         android:id="@+id/icon_frame"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content">
-        <ImageView
+        <android.support.v7.internal.widget.PreferenceImageView
             android:id="@android:id/icon"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_gravity="center"
-        />
+            app:maxWidth="48dp"
+            app:maxHeight="48dp" />
     </FrameLayout>
 
     <RelativeLayout
diff --git a/wear/res/drawable-v23/ws_action_item_background.xml b/wear/res/drawable/ws_action_item_background.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_action_item_background.xml
rename to wear/res/drawable/ws_action_item_background.xml
diff --git a/wear/res/drawable-v23/ws_action_item_icon_background.xml b/wear/res/drawable/ws_action_item_icon_background.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_action_item_icon_background.xml
rename to wear/res/drawable/ws_action_item_icon_background.xml
diff --git a/wear/res/drawable-v23/ws_ic_expand_less_white_22.xml b/wear/res/drawable/ws_ic_expand_less_white_22.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_ic_expand_less_white_22.xml
rename to wear/res/drawable/ws_ic_expand_less_white_22.xml
diff --git a/wear/res/drawable-v23/ws_ic_expand_more_white_22.xml b/wear/res/drawable/ws_ic_expand_more_white_22.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_ic_expand_more_white_22.xml
rename to wear/res/drawable/ws_ic_expand_more_white_22.xml
diff --git a/wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml b/wear/res/drawable/ws_ic_more_horiz_24dp_wht.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_ic_more_horiz_24dp_wht.xml
rename to wear/res/drawable/ws_ic_more_horiz_24dp_wht.xml
diff --git a/wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml b/wear/res/drawable/ws_ic_more_vert_24dp_wht.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_ic_more_vert_24dp_wht.xml
rename to wear/res/drawable/ws_ic_more_vert_24dp_wht.xml
diff --git a/wear/res/drawable-v23/ws_switch_thumb_material_anim.xml b/wear/res/drawable/ws_switch_thumb_material_anim.xml
similarity index 100%
rename from wear/res/drawable-v23/ws_switch_thumb_material_anim.xml
rename to wear/res/drawable/ws_switch_thumb_material_anim.xml
diff --git a/wear/res/layout-v23/ws_action_drawer_item_view.xml b/wear/res/layout/ws_action_drawer_item_view.xml
similarity index 100%
rename from wear/res/layout-v23/ws_action_drawer_item_view.xml
rename to wear/res/layout/ws_action_drawer_item_view.xml
diff --git a/wear/res/layout-v23/ws_action_drawer_peek_view.xml b/wear/res/layout/ws_action_drawer_peek_view.xml
similarity index 100%
rename from wear/res/layout-v23/ws_action_drawer_peek_view.xml
rename to wear/res/layout/ws_action_drawer_peek_view.xml
diff --git a/wear/res/layout-v23/ws_action_drawer_title_view.xml b/wear/res/layout/ws_action_drawer_title_view.xml
similarity index 100%
rename from wear/res/layout-v23/ws_action_drawer_title_view.xml
rename to wear/res/layout/ws_action_drawer_title_view.xml
diff --git a/wear/res/layout-v23/ws_wearable_drawer_view.xml b/wear/res/layout/ws_wearable_drawer_view.xml
similarity index 100%
rename from wear/res/layout-v23/ws_wearable_drawer_view.xml
rename to wear/res/layout/ws_wearable_drawer_view.xml
diff --git a/wear/res/values-v23/styles.xml b/wear/res/values-v23/styles.xml
deleted file mode 100644
index 63ed2d8..0000000
--- a/wear/res/values-v23/styles.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2017 The Android Open Source Project
-
-     Licensed under the Apache License, Version 2.0 (the "License");
-     you may not use this file except in compliance with the License.
-     You may obtain a copy of the License at
-
-          http://www.apache.org/licenses/LICENSE-2.0
-
-     Unless required by applicable law or agreed to in writing, software
-     distributed under the License is distributed on an "AS IS" BASIS,
-     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-     See the License for the specific language governing permissions and
-     limitations under the License.
--->
-<resources>
-    <style name="WsPageIndicatorViewStyle">
-        <item name="wsPageIndicatorDotSpacing">7.8dp</item>
-        <item name="wsPageIndicatorDotRadius">2.1dp</item>
-        <item name="wsPageIndicatorDotRadiusSelected">3.1dp</item>
-        <item name="wsPageIndicatorDotColor">?android:attr/colorForeground</item>
-        <item name="wsPageIndicatorDotColorSelected">?android:attr/colorForeground</item>
-        <item name="wsPageIndicatorDotFadeOutDelay">1000</item>
-        <item name="wsPageIndicatorDotFadeOutDuration">250</item>
-        <item name="wsPageIndicatorDotFadeInDuration">100</item>
-        <item name="wsPageIndicatorDotFadeWhenIdle">true</item>
-        <item name="wsPageIndicatorDotShadowColor">#66000000</item>
-        <item name="wsPageIndicatorDotShadowRadius">1dp</item>
-        <item name="wsPageIndicatorDotShadowDx">0.5dp</item>
-        <item name="wsPageIndicatorDotShadowDy">0.5dp</item>
-    </style>
-
-    <style name="WsWearableActionDrawerItemText">
-        <item name="android:layout_gravity">center_vertical</item>
-        <item name="android:ellipsize">end</item>
-        <item name="android:fontFamily">sans-serif-condensed-light</item>
-        <item name="android:maxLines">3</item>
-        <item name="android:textColor">?android:attr/textColorPrimary</item>
-        <item name="android:textSize">@dimen/ws_action_drawer_item_text_size</item>
-    </style>
-    <style name="WsWearableActionDrawerTitleText" parent="android:TextAppearance.Material.Subhead">
-        <item name="android:alpha">0.7</item>
-    </style>
-</resources>
diff --git a/wear/res/values/styles.xml b/wear/res/values/styles.xml
index 1908946..0a5127d 100644
--- a/wear/res/values/styles.xml
+++ b/wear/res/values/styles.xml
@@ -36,4 +36,31 @@
         <item name="android:elevation">@dimen/ws_wearable_drawer_view_elevation</item>
         <item name="android:background">?android:attr/colorBackgroundFloating</item>
     </style>
+    <style name="WsPageIndicatorViewStyle">
+        <item name="wsPageIndicatorDotSpacing">7.8dp</item>
+        <item name="wsPageIndicatorDotRadius">2.1dp</item>
+        <item name="wsPageIndicatorDotRadiusSelected">3.1dp</item>
+        <item name="wsPageIndicatorDotColor">?android:attr/colorForeground</item>
+        <item name="wsPageIndicatorDotColorSelected">?android:attr/colorForeground</item>
+        <item name="wsPageIndicatorDotFadeOutDelay">1000</item>
+        <item name="wsPageIndicatorDotFadeOutDuration">250</item>
+        <item name="wsPageIndicatorDotFadeInDuration">100</item>
+        <item name="wsPageIndicatorDotFadeWhenIdle">true</item>
+        <item name="wsPageIndicatorDotShadowColor">#66000000</item>
+        <item name="wsPageIndicatorDotShadowRadius">1dp</item>
+        <item name="wsPageIndicatorDotShadowDx">0.5dp</item>
+        <item name="wsPageIndicatorDotShadowDy">0.5dp</item>
+    </style>
+
+    <style name="WsWearableActionDrawerItemText">
+        <item name="android:layout_gravity">center_vertical</item>
+        <item name="android:ellipsize">end</item>
+        <item name="android:fontFamily">sans-serif-condensed-light</item>
+        <item name="android:maxLines">3</item>
+        <item name="android:textColor">?android:attr/textColorPrimary</item>
+        <item name="android:textSize">@dimen/ws_action_drawer_item_text_size</item>
+    </style>
+    <style name="WsWearableActionDrawerTitleText" parent="android:TextAppearance.Material.Subhead">
+        <item name="android:alpha">0.7</item>
+    </style>
 </resources>