Merge "Hide message / remote input references"
diff --git a/compat/api/current.txt b/compat/api/current.txt
index fabc4aa..37fdd22 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -1211,6 +1211,7 @@
     method public E get(long, E);
     method public int indexOfKey(long);
     method public int indexOfValue(E);
+    method public boolean isEmpty();
     method public long keyAt(int);
     method public void put(long, E);
     method public void remove(long);
@@ -1312,6 +1313,7 @@
     method public E get(int, E);
     method public int indexOfKey(int);
     method public int indexOfValue(E);
+    method public boolean isEmpty();
     method public int keyAt(int);
     method public void put(int, E);
     method public void remove(int);
diff --git a/compat/src/main/java/android/support/v13/view/DragAndDropPermissionsCompat.java b/compat/src/main/java/android/support/v13/view/DragAndDropPermissionsCompat.java
index 13ed203..5fe61da 100644
--- a/compat/src/main/java/android/support/v13/view/DragAndDropPermissionsCompat.java
+++ b/compat/src/main/java/android/support/v13/view/DragAndDropPermissionsCompat.java
@@ -20,57 +20,16 @@
 
 import android.app.Activity;
 import android.os.Build;
-import android.support.annotation.RequiresApi;
+import android.support.annotation.Nullable;
 import android.support.annotation.RestrictTo;
 import android.view.DragAndDropPermissions;
 import android.view.DragEvent;
 
 /**
- * Helper for accessing features in {@link android.view.DragAndDropPermissions}
- * introduced after API level 13 in a backwards compatible fashion.
+ * Helper for accessing features in {@link android.view.DragAndDropPermissions} a backwards
+ * compatible fashion.
  */
 public final class DragAndDropPermissionsCompat {
-
-    interface DragAndDropPermissionsCompatImpl {
-        Object request(Activity activity, DragEvent dragEvent);
-        void release(Object dragAndDropPermissions);
-    }
-
-    static class BaseDragAndDropPermissionsCompatImpl implements DragAndDropPermissionsCompatImpl {
-        @Override
-        public Object request(Activity activity, DragEvent dragEvent) {
-            return null;
-        }
-
-        @Override
-        public void release(Object dragAndDropPermissions) {
-            // no-op
-        }
-    }
-
-    @RequiresApi(24)
-    static class Api24DragAndDropPermissionsCompatImpl
-            extends BaseDragAndDropPermissionsCompatImpl {
-        @Override
-        public Object request(Activity activity, DragEvent dragEvent) {
-            return activity.requestDragAndDropPermissions(dragEvent);
-        }
-
-        @Override
-        public void release(Object dragAndDropPermissions) {
-            ((DragAndDropPermissions) dragAndDropPermissions).release();
-        }
-    }
-
-    private static DragAndDropPermissionsCompatImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 24) {
-            IMPL = new Api24DragAndDropPermissionsCompatImpl();
-        } else {
-            IMPL = new BaseDragAndDropPermissionsCompatImpl();
-        }
-    }
-
     private Object mDragAndDropPermissions;
 
     private DragAndDropPermissionsCompat(Object dragAndDropPermissions) {
@@ -79,18 +38,24 @@
 
     /** @hide */
     @RestrictTo(LIBRARY_GROUP)
+    @Nullable
     public static DragAndDropPermissionsCompat request(Activity activity, DragEvent dragEvent) {
-        Object dragAndDropPermissions = IMPL.request(activity, dragEvent);
-        if (dragAndDropPermissions != null) {
-            return new DragAndDropPermissionsCompat(dragAndDropPermissions);
+        if (Build.VERSION.SDK_INT >= 24) {
+            DragAndDropPermissions dragAndDropPermissions =
+                    activity.requestDragAndDropPermissions(dragEvent);
+            if (dragAndDropPermissions != null) {
+                return new DragAndDropPermissionsCompat(dragAndDropPermissions);
+            }
         }
         return null;
     }
 
-    /*
+    /**
      * Revoke the permission grant explicitly.
      */
     public void release() {
-        IMPL.release(mDragAndDropPermissions);
+        if (Build.VERSION.SDK_INT >= 24) {
+            ((DragAndDropPermissions) mDragAndDropPermissions).release();
+        }
     }
 }
diff --git a/compat/src/main/java/android/support/v13/view/DragStartHelper.java b/compat/src/main/java/android/support/v13/view/DragStartHelper.java
index 85bc2f3..f8aed92 100644
--- a/compat/src/main/java/android/support/v13/view/DragStartHelper.java
+++ b/compat/src/main/java/android/support/v13/view/DragStartHelper.java
@@ -69,8 +69,8 @@
  * </pre>
  */
 public class DragStartHelper {
-    final private View mView;
-    final private OnDragStartListener mListener;
+    private final View mView;
+    private final OnDragStartListener mListener;
 
     private int mLastTouchX, mLastTouchY;
     private boolean mDragging;
diff --git a/compat/src/main/java/android/support/v13/view/inputmethod/EditorInfoCompat.java b/compat/src/main/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
index 92743c2..309877d 100644
--- a/compat/src/main/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
+++ b/compat/src/main/java/android/support/v13/view/inputmethod/EditorInfoCompat.java
@@ -16,7 +16,6 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.support.annotation.RequiresApi;
 import android.os.Build;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
@@ -24,8 +23,7 @@
 import android.view.inputmethod.EditorInfo;
 
 /**
- * Helper for accessing features in {@link EditorInfo} introduced after API level 13 in a backwards
- * compatible fashion.
+ * Helper for accessing features in {@link EditorInfo} in a backwards compatible fashion.
  */
 public final class EditorInfoCompat {
 
@@ -69,63 +67,10 @@
      */
     public static final int IME_FLAG_FORCE_ASCII = 0x80000000;
 
-    private interface EditorInfoCompatImpl {
-        void setContentMimeTypes(@NonNull EditorInfo editorInfo,
-                @Nullable String[] contentMimeTypes);
-        @NonNull
-        String[] getContentMimeTypes(@NonNull EditorInfo editorInfo);
-    }
-
     private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
-    private static final class EditorInfoCompatBaseImpl implements EditorInfoCompatImpl {
-        private static String CONTENT_MIME_TYPES_KEY =
-                "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
-
-        @Override
-        public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
-                @Nullable String[] contentMimeTypes) {
-            if (editorInfo.extras == null) {
-                editorInfo.extras = new Bundle();
-            }
-            editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
-        }
-
-        @NonNull
-        @Override
-        public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
-            if (editorInfo.extras == null) {
-                return EMPTY_STRING_ARRAY;
-            }
-            String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
-            return result != null ? result : EMPTY_STRING_ARRAY;
-        }
-    }
-
-    @RequiresApi(25)
-    private static final class EditorInfoCompatApi25Impl implements EditorInfoCompatImpl {
-        @Override
-        public void setContentMimeTypes(@NonNull EditorInfo editorInfo,
-                @Nullable String[] contentMimeTypes) {
-            editorInfo.contentMimeTypes = contentMimeTypes;
-        }
-
-        @NonNull
-        @Override
-        public String[] getContentMimeTypes(@NonNull EditorInfo editorInfo) {
-            final String[] result = editorInfo.contentMimeTypes;
-            return result != null ? result : EMPTY_STRING_ARRAY;
-        }
-    }
-
-    private static final EditorInfoCompatImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 25) {
-            IMPL = new EditorInfoCompatApi25Impl();
-        } else {
-            IMPL = new EditorInfoCompatBaseImpl();
-        }
-    }
+    private static final String CONTENT_MIME_TYPES_KEY =
+            "android.support.v13.view.inputmethod.EditorInfoCompat.CONTENT_MIME_TYPES";
 
     /**
      * Sets MIME types that can be accepted by the target editor if the IME calls
@@ -140,7 +85,14 @@
      */
     public static void setContentMimeTypes(@NonNull EditorInfo editorInfo,
             @Nullable String[] contentMimeTypes) {
-        IMPL.setContentMimeTypes(editorInfo, contentMimeTypes);
+        if (Build.VERSION.SDK_INT >= 25) {
+            editorInfo.contentMimeTypes = contentMimeTypes;
+        } else {
+            if (editorInfo.extras == null) {
+                editorInfo.extras = new Bundle();
+            }
+            editorInfo.extras.putStringArray(CONTENT_MIME_TYPES_KEY, contentMimeTypes);
+        }
     }
 
     /**
@@ -155,7 +107,16 @@
      */
     @NonNull
     public static String[] getContentMimeTypes(EditorInfo editorInfo) {
-        return IMPL.getContentMimeTypes(editorInfo);
+        if (Build.VERSION.SDK_INT >= 25) {
+            final String[] result = editorInfo.contentMimeTypes;
+            return result != null ? result : EMPTY_STRING_ARRAY;
+        } else {
+            if (editorInfo.extras == null) {
+                return EMPTY_STRING_ARRAY;
+            }
+            String[] result = editorInfo.extras.getStringArray(CONTENT_MIME_TYPES_KEY);
+            return result != null ? result : EMPTY_STRING_ARRAY;
+        }
     }
 
 }
diff --git a/compat/src/main/java/android/support/v13/view/inputmethod/InputConnectionCompat.java b/compat/src/main/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
index 5999575..d77389b 100644
--- a/compat/src/main/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
+++ b/compat/src/main/java/android/support/v13/view/inputmethod/InputConnectionCompat.java
@@ -16,7 +16,6 @@
 
 package android.support.v13.view.inputmethod;
 
-import android.support.annotation.RequiresApi;
 import android.content.ClipDescription;
 import android.net.Uri;
 import android.os.Build;
@@ -36,138 +35,50 @@
  */
 public final class InputConnectionCompat {
 
-    private interface InputConnectionCompatImpl {
-        boolean commitContent(@NonNull InputConnection inputConnection,
-                @NonNull InputContentInfoCompat inputContentInfo, int flags, @Nullable Bundle opts);
+    private static final String COMMIT_CONTENT_ACTION =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
+    private static final String COMMIT_CONTENT_CONTENT_URI_KEY =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_URI";
+    private static final String COMMIT_CONTENT_DESCRIPTION_KEY =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_DESCRIPTION";
+    private static final String COMMIT_CONTENT_LINK_URI_KEY =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_LINK_URI";
+    private static final String COMMIT_CONTENT_OPTS_KEY =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_OPTS";
+    private static final String COMMIT_CONTENT_FLAGS_KEY =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_FLAGS";
+    private static final String COMMIT_CONTENT_RESULT_RECEIVER =
+            "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_RESULT_RECEIVER";
 
-        @NonNull
-        InputConnection createWrapper(@NonNull InputConnection ic,
-                @NonNull EditorInfo editorInfo, @NonNull OnCommitContentListener callback);
-    }
-
-    static final class InputContentInfoCompatBaseImpl implements InputConnectionCompatImpl {
-
-        private static String COMMIT_CONTENT_ACTION =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.COMMIT_CONTENT";
-        private static String COMMIT_CONTENT_CONTENT_URI_KEY =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_URI";
-        private static String COMMIT_CONTENT_DESCRIPTION_KEY =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_DESCRIPTION";
-        private static String COMMIT_CONTENT_LINK_URI_KEY =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_LINK_URI";
-        private static String COMMIT_CONTENT_OPTS_KEY =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_OPTS";
-        private static String COMMIT_CONTENT_FLAGS_KEY =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_FLAGS";
-        private static String COMMIT_CONTENT_RESULT_RECEIVER =
-                "android.support.v13.view.inputmethod.InputConnectionCompat.CONTENT_RESULT_RECEIVER";
-
-        @Override
-        public boolean commitContent(@NonNull InputConnection inputConnection,
-                @NonNull InputContentInfoCompat inputContentInfo, int flags,
-                @Nullable Bundle opts) {
-            final Bundle params = new Bundle();
-            params.putParcelable(COMMIT_CONTENT_CONTENT_URI_KEY, inputContentInfo.getContentUri());
-            params.putParcelable(COMMIT_CONTENT_DESCRIPTION_KEY, inputContentInfo.getDescription());
-            params.putParcelable(COMMIT_CONTENT_LINK_URI_KEY, inputContentInfo.getLinkUri());
-            params.putInt(COMMIT_CONTENT_FLAGS_KEY, flags);
-            params.putParcelable(COMMIT_CONTENT_OPTS_KEY, opts);
-            // TODO: Support COMMIT_CONTENT_RESULT_RECEIVER.
-            return inputConnection.performPrivateCommand(COMMIT_CONTENT_ACTION, params);
+    static boolean handlePerformPrivateCommand(
+            @Nullable String action,
+            @NonNull Bundle data,
+            @NonNull OnCommitContentListener onCommitContentListener) {
+        if (!TextUtils.equals(COMMIT_CONTENT_ACTION, action)) {
+            return false;
         }
-
-        @NonNull
-        @Override
-        public InputConnection createWrapper(@NonNull InputConnection ic,
-                @NonNull EditorInfo editorInfo,
-                @NonNull OnCommitContentListener onCommitContentListener) {
-            String[] contentMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
-            if (contentMimeTypes.length == 0) {
-                return ic;
+        if (data == null) {
+            return false;
+        }
+        ResultReceiver resultReceiver = null;
+        boolean result = false;
+        try {
+            resultReceiver = data.getParcelable(COMMIT_CONTENT_RESULT_RECEIVER);
+            final Uri contentUri = data.getParcelable(COMMIT_CONTENT_CONTENT_URI_KEY);
+            final ClipDescription description = data.getParcelable(
+                    COMMIT_CONTENT_DESCRIPTION_KEY);
+            final Uri linkUri = data.getParcelable(COMMIT_CONTENT_LINK_URI_KEY);
+            final int flags = data.getInt(COMMIT_CONTENT_FLAGS_KEY);
+            final Bundle opts = data.getParcelable(COMMIT_CONTENT_OPTS_KEY);
+            final InputContentInfoCompat inputContentInfo =
+                    new InputContentInfoCompat(contentUri, description, linkUri);
+            result = onCommitContentListener.onCommitContent(inputContentInfo, flags, opts);
+        } finally {
+            if (resultReceiver != null) {
+                resultReceiver.send(result ? 1 : 0, null);
             }
-            final OnCommitContentListener listener = onCommitContentListener;
-            return new InputConnectionWrapper(ic, false /* mutable */) {
-                @Override
-                public boolean performPrivateCommand(String action, Bundle data) {
-                    if (InputContentInfoCompatBaseImpl.handlePerformPrivateCommand(action, data,
-                            listener)) {
-                        return true;
-                    }
-                    return super.performPrivateCommand(action, data);
-                }
-            };
         }
-
-        static boolean handlePerformPrivateCommand(
-                @Nullable String action,
-                @NonNull Bundle data,
-                @NonNull OnCommitContentListener onCommitContentListener) {
-            if (!TextUtils.equals(COMMIT_CONTENT_ACTION, action)) {
-                return false;
-            }
-            if (data == null) {
-                return false;
-            }
-            ResultReceiver resultReceiver = null;
-            boolean result = false;
-            try {
-                resultReceiver = data.getParcelable(COMMIT_CONTENT_RESULT_RECEIVER);
-                final Uri contentUri = data.getParcelable(COMMIT_CONTENT_CONTENT_URI_KEY);
-                final ClipDescription description = data.getParcelable(
-                        COMMIT_CONTENT_DESCRIPTION_KEY);
-                final Uri linkUri = data.getParcelable(COMMIT_CONTENT_LINK_URI_KEY);
-                final int flags = data.getInt(COMMIT_CONTENT_FLAGS_KEY);
-                final Bundle opts = data.getParcelable(COMMIT_CONTENT_OPTS_KEY);
-                final InputContentInfoCompat inputContentInfo =
-                        new InputContentInfoCompat(contentUri, description, linkUri);
-                result = onCommitContentListener.onCommitContent(inputContentInfo, flags, opts);
-            } finally {
-                if (resultReceiver != null) {
-                    resultReceiver.send(result ? 1 : 0, null);
-                }
-            }
-            return result;
-        }
-    }
-
-    @RequiresApi(25)
-    private static final class InputContentInfoCompatApi25Impl
-            implements InputConnectionCompatImpl {
-        @Override
-        public boolean commitContent(@NonNull InputConnection inputConnection,
-                @NonNull InputContentInfoCompat inputContentInfo, int flags,
-                @Nullable Bundle opts) {
-            return inputConnection.commitContent((InputContentInfo) inputContentInfo.unwrap(),
-                    flags, opts);
-        }
-
-        @Nullable
-        @Override
-        public InputConnection createWrapper(
-                @Nullable InputConnection inputConnection, @NonNull EditorInfo editorInfo,
-                @Nullable OnCommitContentListener onCommitContentListener) {
-            final OnCommitContentListener listener = onCommitContentListener;
-            return new InputConnectionWrapper(inputConnection, false /* mutable */) {
-                @Override
-                public boolean commitContent(InputContentInfo inputContentInfo, int flags,
-                        Bundle opts) {
-                    if (listener.onCommitContent(InputContentInfoCompat.wrap(inputContentInfo),
-                            flags, opts)) {
-                        return true;
-                    }
-                    return super.commitContent(inputContentInfo, flags, opts);
-                }
-            };
-        }
-    }
-
-    private static final InputConnectionCompatImpl IMPL;
-    static {
-        if (Build.VERSION.SDK_INT >= 25) {
-            IMPL = new InputContentInfoCompatApi25Impl();
-        } else {
-            IMPL = new InputContentInfoCompatBaseImpl();
-        }
+        return result;
     }
 
     /**
@@ -196,7 +107,19 @@
             return false;
         }
 
-        return IMPL.commitContent(inputConnection, inputContentInfo, flags, opts);
+        if (Build.VERSION.SDK_INT >= 25) {
+            return inputConnection.commitContent(
+                    (InputContentInfo) inputContentInfo.unwrap(), flags, opts);
+        } else {
+            final Bundle params = new Bundle();
+            params.putParcelable(COMMIT_CONTENT_CONTENT_URI_KEY, inputContentInfo.getContentUri());
+            params.putParcelable(COMMIT_CONTENT_DESCRIPTION_KEY, inputContentInfo.getDescription());
+            params.putParcelable(COMMIT_CONTENT_LINK_URI_KEY, inputContentInfo.getLinkUri());
+            params.putInt(COMMIT_CONTENT_FLAGS_KEY, flags);
+            params.putParcelable(COMMIT_CONTENT_OPTS_KEY, opts);
+            // TODO: Support COMMIT_CONTENT_RESULT_RECEIVER.
+            return inputConnection.performPrivateCommand(COMMIT_CONTENT_ACTION, params);
+        }
     }
 
     /**
@@ -276,7 +199,35 @@
         if (onCommitContentListener == null) {
             throw new IllegalArgumentException("onCommitContentListener must be non-null");
         }
-        return IMPL.createWrapper(inputConnection, editorInfo, onCommitContentListener);
+        if (Build.VERSION.SDK_INT >= 25) {
+            final OnCommitContentListener listener = onCommitContentListener;
+            return new InputConnectionWrapper(inputConnection, false /* mutable */) {
+                @Override
+                public boolean commitContent(InputContentInfo inputContentInfo, int flags,
+                        Bundle opts) {
+                    if (listener.onCommitContent(InputContentInfoCompat.wrap(inputContentInfo),
+                            flags, opts)) {
+                        return true;
+                    }
+                    return super.commitContent(inputContentInfo, flags, opts);
+                }
+            };
+        } else {
+            String[] contentMimeTypes = EditorInfoCompat.getContentMimeTypes(editorInfo);
+            if (contentMimeTypes.length == 0) {
+                return inputConnection;
+            }
+            final OnCommitContentListener listener = onCommitContentListener;
+            return new InputConnectionWrapper(inputConnection, false /* mutable */) {
+                @Override
+                public boolean performPrivateCommand(String action, Bundle data) {
+                    if (InputConnectionCompat.handlePerformPrivateCommand(action, data, listener)) {
+                        return true;
+                    }
+                    return super.performPrivateCommand(action, data);
+                }
+            };
+        }
     }
 
 }
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 9d15be1..32355e7 100644
--- a/compat/src/main/java/android/support/v4/app/ActivityCompat.java
+++ b/compat/src/main/java/android/support/v4/app/ActivityCompat.java
@@ -538,6 +538,7 @@
      * URIs. {@code null} if no content URIs are associated with the event or if permissions could
      * not be granted.
      */
+    @Nullable
     public static DragAndDropPermissionsCompat requestDragAndDropPermissions(Activity activity,
             DragEvent dragEvent) {
         return DragAndDropPermissionsCompat.request(activity, dragEvent);
diff --git a/compat/src/main/java/android/support/v4/util/LongSparseArray.java b/compat/src/main/java/android/support/v4/util/LongSparseArray.java
index 25b6bb9..febb5d5 100644
--- a/compat/src/main/java/android/support/v4/util/LongSparseArray.java
+++ b/compat/src/main/java/android/support/v4/util/LongSparseArray.java
@@ -235,6 +235,14 @@
     }
 
     /**
+     * Return true if size() is 0.
+     * @return true if size() is 0.
+     */
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
      * Given an index in the range <code>0...size()-1</code>, returns
      * the key from the <code>index</code>th key-value mapping that this
      * LongSparseArray stores.
diff --git a/compat/src/main/java/android/support/v4/util/SparseArrayCompat.java b/compat/src/main/java/android/support/v4/util/SparseArrayCompat.java
index aedc4ad..5238cf0 100644
--- a/compat/src/main/java/android/support/v4/util/SparseArrayCompat.java
+++ b/compat/src/main/java/android/support/v4/util/SparseArrayCompat.java
@@ -228,6 +228,14 @@
     }
 
     /**
+     * Return true if size() is 0.
+     * @return true if size() is 0.
+     */
+    public boolean isEmpty() {
+        return size() == 0;
+    }
+
+    /**
      * Given an index in the range <code>0...size()-1</code>, returns
      * the key from the <code>index</code>th key-value mapping that this
      * SparseArray stores.
diff --git a/compat/tests/java/android/support/v4/util/LongSparseArrayTest.java b/compat/tests/java/android/support/v4/util/LongSparseArrayTest.java
new file mode 100644
index 0000000..663ea48
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/LongSparseArrayTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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 android.support.v4.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class LongSparseArrayTest {
+    @Test
+    public void isEmpty() throws Exception {
+        LongSparseArray<String> LongSparseArray = new LongSparseArray<>();
+        assertTrue(LongSparseArray.isEmpty()); // Newly created LongSparseArray should be empty
+
+        // Adding elements should change state from empty to not empty.
+        for (long i = 0L; i < 5L; i++) {
+            LongSparseArray.put(i, Long.toString(i));
+            assertFalse(LongSparseArray.isEmpty());
+        }
+        LongSparseArray.clear();
+        assertTrue(LongSparseArray.isEmpty()); // A cleared LongSparseArray should be empty.
+
+
+        long key1 = 1L, key2 = 2L;
+        String value1 = "some value", value2 = "some other value";
+        LongSparseArray.append(key1, value1);
+        assertFalse(LongSparseArray.isEmpty()); // has 1 element.
+        LongSparseArray.append(key2, value2);
+        assertFalse(LongSparseArray.isEmpty());  // has 2 elements.
+        assertFalse(LongSparseArray.isEmpty());  // consecutive calls should be OK.
+
+        LongSparseArray.remove(key1);
+        assertFalse(LongSparseArray.isEmpty()); // has 1 element.
+        LongSparseArray.remove(key2);
+        assertTrue(LongSparseArray.isEmpty());
+    }
+
+}
diff --git a/compat/tests/java/android/support/v4/util/SparseArrayCompatTest.java b/compat/tests/java/android/support/v4/util/SparseArrayCompatTest.java
new file mode 100644
index 0000000..122c89b
--- /dev/null
+++ b/compat/tests/java/android/support/v4/util/SparseArrayCompatTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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 android.support.v4.util;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class SparseArrayCompatTest {
+    @Test
+    public void isEmpty() throws Exception {
+        SparseArrayCompat<String> sparseArrayCompat = new SparseArrayCompat<>();
+        assertTrue(sparseArrayCompat.isEmpty()); // Newly created SparseArrayCompat should be empty
+
+        // Adding elements should change state from empty to not empty.
+        for (int i = 0; i < 5; i++) {
+            sparseArrayCompat.put(i, Integer.toString(i));
+            assertFalse(sparseArrayCompat.isEmpty());
+        }
+        sparseArrayCompat.clear();
+        assertTrue(sparseArrayCompat.isEmpty()); // A cleared SparseArrayCompat should be empty.
+
+
+        int key1 = 1, key2 = 2;
+        String value1 = "some value", value2 = "some other value";
+        sparseArrayCompat.append(key1, value1);
+        assertFalse(sparseArrayCompat.isEmpty()); // has 1 element.
+        sparseArrayCompat.append(key2, value2);
+        assertFalse(sparseArrayCompat.isEmpty());  // has 2 elements.
+        assertFalse(sparseArrayCompat.isEmpty());  // consecutive calls should be OK.
+
+        sparseArrayCompat.remove(key1);
+        assertFalse(sparseArrayCompat.isEmpty()); // has 1 element.
+        sparseArrayCompat.remove(key2);
+        assertTrue(sparseArrayCompat.isEmpty());
+    }
+}
diff --git a/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java b/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
index 8ec3eee..78555aa 100644
--- a/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
+++ b/core-utils/java/android/support/v4/content/WakefulBroadcastReceiver.java
@@ -34,6 +34,9 @@
  * for you; you must request the {@link android.Manifest.permission#WAKE_LOCK}
  * permission to use it.</p>
  *
+ * <p>Wakelocks held by this class are reported to tools as
+ * {@code "androidx.core:wake:<component-name>"}.</p>
+ *
  * <h3>Example</h3>
  *
  * <p>A {@link WakefulBroadcastReceiver} uses the method
@@ -103,7 +106,7 @@
 
             PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
             PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
-                    "wake:" + comp.flattenToShortString());
+                    "androidx.core:wake:" + comp.flattenToShortString());
             wl.setReferenceCounted(false);
             wl.acquire(60 * 1000);
             sActiveWakeLocks.put(id, wl);
diff --git a/fragment/src/main/java/android/support/v4/app/Fragment.java b/fragment/src/main/java/android/support/v4/app/Fragment.java
index 5b560cd..ce9bc4b 100644
--- a/fragment/src/main/java/android/support/v4/app/Fragment.java
+++ b/fragment/src/main/java/android/support/v4/app/Fragment.java
@@ -575,7 +575,6 @@
     /**
      * Return the {@link Context} this fragment is currently associated with.
      */
-    @Nullable
     public Context getContext() {
         return mHost == null ? null : mHost.getContext();
     }
@@ -585,7 +584,6 @@
      * May return {@code null} if the fragment is associated with a {@link Context}
      * instead.
      */
-    @Nullable
     final public FragmentActivity getActivity() {
         return mHost == null ? null : (FragmentActivity) mHost.getActivity();
     }
@@ -594,7 +592,6 @@
      * Return the host object of this fragment. May return {@code null} if the fragment
      * isn't currently being hosted.
      */
-    @Nullable
     final public Object getHost() {
         return mHost == null ? null : mHost.onGetHost();
     }
@@ -655,7 +652,6 @@
      * <p>If this Fragment is a child of another Fragment, the FragmentManager
      * returned here will be the parent's {@link #getChildFragmentManager()}.
      */
-    @Nullable
     final public FragmentManager getFragmentManager() {
         return mFragmentManager;
     }
diff --git a/graphics/drawable/animated/src/main/java/android/support/graphics/drawable/AnimatorInflaterCompat.java b/graphics/drawable/animated/src/main/java/android/support/graphics/drawable/AnimatorInflaterCompat.java
index cfededb..da522f6 100644
--- a/graphics/drawable/animated/src/main/java/android/support/graphics/drawable/AnimatorInflaterCompat.java
+++ b/graphics/drawable/animated/src/main/java/android/support/graphics/drawable/AnimatorInflaterCompat.java
@@ -463,7 +463,6 @@
         // the previously sampled contours' total length.
         for (int i = 0; i < numPoints; ++i) {
             pathMeasure.getPosTan(currentDistance, position, null);
-            pathMeasure.getPosTan(currentDistance, position, null);
 
             mX[i] = position[0];
             mY[i] = position[1];
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/SqlParser.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/SqlParser.kt
index c5c5f17..0bd2f49 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/SqlParser.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/parser/SqlParser.kt
@@ -214,7 +214,7 @@
         return when (this) {
             TEXT -> listOf(env.elementUtils.getTypeElement("java.lang.String").asType())
             INTEGER -> withBoxedTypes(env, TypeKind.INT, TypeKind.BYTE, TypeKind.CHAR,
-                    TypeKind.BOOLEAN, TypeKind.LONG, TypeKind.SHORT)
+                    TypeKind.LONG, TypeKind.SHORT)
             REAL -> withBoxedTypes(env, TypeKind.DOUBLE, TypeKind.FLOAT)
             BLOB -> listOf(typeUtils.getArrayType(
                     typeUtils.getPrimitiveType(TypeKind.BYTE)))
diff --git a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/TypeAdapterStore.kt b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/TypeAdapterStore.kt
index 7abad86..62a24ad 100644
--- a/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/TypeAdapterStore.kt
+++ b/room/compiler/src/main/kotlin/android/arch/persistence/room/solver/TypeAdapterStore.kt
@@ -160,7 +160,11 @@
         }
         val targetTypes = targetTypeMirrorsFor(affinity)
         val binder = findTypeConverter(input, targetTypes) ?: return null
-        return CompositeAdapter(input, getAllColumnAdapters(binder.to).first(), binder, null)
+        // columnAdapter should not be null but we are receiving errors on crash in `first()` so
+        // this safeguard allows us to dispatch the real problem to the user (e.g. why we couldn't
+        // find the right adapter)
+        val columnAdapter = getAllColumnAdapters(binder.to).firstOrNull() ?: return null
+        return CompositeAdapter(input, columnAdapter, binder, null)
     }
 
     /**
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
index 82a3583..7cb8b60 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/dao/UserDao.java
@@ -164,6 +164,12 @@
     @Query("SELECT COUNT(*) from user")
     public abstract int count();
 
+    @Query("SELECT mAdmin from User where mId = :uid")
+    public abstract boolean isAdmin(int uid);
+
+    @Query("SELECT mAdmin from User where mId = :uid")
+    public abstract LiveData<Boolean> isAdminLiveData(int uid);
+
     public void insertBothByRunnable(final User a, final User b) {
         mDatabase.runInTransaction(new Runnable() {
             @Override
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
index d78411f..d073598 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/LiveDataQueryTest.java
@@ -315,6 +315,24 @@
         assertThat(weakLiveData.get(), nullValue());
     }
 
+    @Test
+    public void booleanLiveData() throws ExecutionException, InterruptedException,
+            TimeoutException {
+        User user = TestUtil.createUser(3);
+        user.setAdmin(false);
+        LiveData<Boolean> adminLiveData = mUserDao.isAdminLiveData(3);
+        final TestLifecycleOwner lifecycleOwner = new TestLifecycleOwner();
+        lifecycleOwner.handleEvent(Lifecycle.Event.ON_START);
+        final TestObserver<Boolean> observer = new TestObserver<>();
+        observe(adminLiveData, lifecycleOwner, observer);
+        assertThat(observer.get(), is(nullValue()));
+        mUserDao.insert(user);
+        assertThat(observer.get(), is(false));
+        user.setAdmin(true);
+        mUserDao.insertOrReplace(user);
+        assertThat(observer.get(), is(true));
+    }
+
     private void observe(final LiveData liveData, final LifecycleOwner provider,
             final Observer observer) throws ExecutionException, InterruptedException {
         FutureTask<Void> futureTask = new FutureTask<>(new Callable<Void>() {
diff --git a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index de45ebb..4ca81ad 100644
--- a/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/android/arch/persistence/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -267,6 +267,18 @@
     }
 
     @Test
+    public void returnBoolean() {
+        User user1 = TestUtil.createUser(1);
+        User user2 = TestUtil.createUser(2);
+        user1.setAdmin(true);
+        user2.setAdmin(false);
+        mUserDao.insert(user1);
+        mUserDao.insert(user2);
+        assertThat(mUserDao.isAdmin(1), is(true));
+        assertThat(mUserDao.isAdmin(2), is(false));
+    }
+
+    @Test
     public void findByCollateNoCase() {
         User user = TestUtil.createUser(3);
         user.setCustomField("abc");
diff --git a/room/runtime/src/androidTest/java/android/arch/persistence/room/migration/TableInfoTest.java b/room/runtime/src/androidTest/java/android/arch/persistence/room/migration/TableInfoTest.java
index d88c02f..0eb35f6 100644
--- a/room/runtime/src/androidTest/java/android/arch/persistence/room/migration/TableInfoTest.java
+++ b/room/runtime/src/androidTest/java/android/arch/persistence/room/migration/TableInfoTest.java
@@ -31,6 +31,7 @@
 import android.support.test.InstrumentationRegistry;
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
+import android.util.Pair;
 
 import org.junit.After;
 import org.junit.Test;
@@ -41,6 +42,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -209,6 +211,28 @@
         ));
     }
 
+    @Test
+    public void compatColumnTypes() {
+        // see:https://www.sqlite.org/datatype3.html 3.1
+        List<Pair<String, String>> testCases = Arrays.asList(
+                new Pair<>("TINYINT", "integer"),
+                new Pair<>("VARCHAR", "text"),
+                new Pair<>("DOUBLE", "real"),
+                new Pair<>("BOOLEAN", "numeric"),
+                new Pair<>("FLOATING POINT", "integer")
+        );
+        for (Pair<String, String> testCase : testCases) {
+            mDb = createDatabase(
+                    "CREATE TABLE foo (id INTEGER PRIMARY KEY AUTOINCREMENT,"
+                            + "name " + testCase.first + ")");
+            TableInfo info = TableInfo.read(mDb, "foo");
+            assertThat(info, is(new TableInfo("foo",
+                    toMap(new TableInfo.Column("id", "INTEGER", false, 1),
+                            new TableInfo.Column("name", testCase.second, false, 0)),
+                    Collections.<TableInfo.ForeignKey>emptySet())));
+        }
+    }
+
     private static Map<String, TableInfo.Column> toMap(TableInfo.Column... columns) {
         Map<String, TableInfo.Column> result = new HashMap<>();
         for (TableInfo.Column column : columns) {
diff --git a/room/runtime/src/main/java/android/arch/persistence/room/util/TableInfo.java b/room/runtime/src/main/java/android/arch/persistence/room/util/TableInfo.java
index a115147..19d9853 100644
--- a/room/runtime/src/main/java/android/arch/persistence/room/util/TableInfo.java
+++ b/room/runtime/src/main/java/android/arch/persistence/room/util/TableInfo.java
@@ -17,6 +17,7 @@
 package android.arch.persistence.room.util;
 
 import android.arch.persistence.db.SupportSQLiteDatabase;
+import android.arch.persistence.room.ColumnInfo;
 import android.database.Cursor;
 import android.os.Build;
 import android.support.annotation.NonNull;
@@ -28,6 +29,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 import java.util.TreeMap;
@@ -44,7 +46,8 @@
  * @hide
  */
 @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
-@SuppressWarnings({"WeakerAccess", "unused", "TryFinallyCanBeTryWithResources"})
+@SuppressWarnings({"WeakerAccess", "unused", "TryFinallyCanBeTryWithResources",
+        "SimplifiableIfStatement"})
 // if you change this class, you must change TableInfoWriter.kt
 public class TableInfo {
     /**
@@ -313,6 +316,14 @@
          */
         public final String type;
         /**
+         * The column type after it is normalized to one of the basic types according to
+         * https://www.sqlite.org/datatype3.html Section 3.1.
+         * <p>
+         * This is the value Room uses for equality check.
+         */
+        @ColumnInfo.SQLiteTypeAffinity
+        public final int affinity;
+        /**
          * Whether or not the column can be NULL.
          */
         public final boolean notNull;
@@ -337,6 +348,40 @@
             this.type = type;
             this.notNull = notNull;
             this.primaryKeyPosition = primaryKeyPosition;
+            this.affinity = findAffinity(type);
+        }
+
+        /**
+         * Implements https://www.sqlite.org/datatype3.html section 3.1
+         *
+         * @param type The type that was given to the sqlite
+         * @return The normalized type which is one of the 5 known affinities
+         */
+        @ColumnInfo.SQLiteTypeAffinity
+        private static int findAffinity(@Nullable String type) {
+            if (type == null) {
+                return ColumnInfo.BLOB;
+            }
+            String uppercaseType = type.toUpperCase(Locale.US);
+            if (uppercaseType.contains("INT")) {
+                return ColumnInfo.INTEGER;
+            }
+            if (uppercaseType.contains("CHAR")
+                    || uppercaseType.contains("CLOB")
+                    || uppercaseType.contains("TEXT")) {
+                return ColumnInfo.TEXT;
+            }
+            if (uppercaseType.contains("BLOB")) {
+                return ColumnInfo.BLOB;
+            }
+            if (uppercaseType.contains("REAL")
+                    || uppercaseType.contains("FLOA")
+                    || uppercaseType.contains("DOUB")) {
+                return ColumnInfo.REAL;
+            }
+            // sqlite returns NUMERIC here but it is like a catch all. We already
+            // have UNDEFINED so it is better to use UNDEFINED for consistency.
+            return ColumnInfo.UNDEFINED;
         }
 
         @Override
@@ -354,7 +399,7 @@
             if (!name.equals(column.name)) return false;
             //noinspection SimplifiableIfStatement
             if (notNull != column.notNull) return false;
-            return type != null ? type.equalsIgnoreCase(column.type) : column.type == null;
+            return affinity == column.affinity;
         }
 
         /**
@@ -369,7 +414,7 @@
         @Override
         public int hashCode() {
             int result = name.hashCode();
-            result = 31 * result + (type != null ? type.hashCode() : 0);
+            result = 31 * result + affinity;
             result = 31 * result + (notNull ? 1231 : 1237);
             result = 31 * result + primaryKeyPosition;
             return result;
@@ -380,6 +425,7 @@
             return "Column{"
                     + "name='" + name + '\''
                     + ", type='" + type + '\''
+                    + ", affinity='" + affinity + '\''
                     + ", notNull=" + notNull
                     + ", primaryKeyPosition=" + primaryKeyPosition
                     + '}';
@@ -472,7 +518,7 @@
         }
 
         @Override
-        public int compareTo(ForeignKeyWithSequence o) {
+        public int compareTo(@NonNull ForeignKeyWithSequence o) {
             final int idCmp = mId - o.mId;
             if (idCmp == 0) {
                 return mSequence - o.mSequence;
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 34acf93..c98f1d2 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
@@ -36,13 +36,29 @@
     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";
+
+    /**
+     * Subtype indicating that this content is the current value for a slider or progress.
+     */
+    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/src/main/java/androidx/app/slice/widget/RowContent.java b/slices/view/src/main/java/androidx/app/slice/widget/RowContent.java
index fb927ff..0e730a5 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
@@ -17,8 +17,10 @@
 package androidx.app.slice.widget;
 
 import static android.app.slice.Slice.HINT_TITLE;
+import static android.app.slice.Slice.SUBTYPE_SLIDER;
 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_REMOTE_INPUT;
 import static android.app.slice.SliceItem.FORMAT_SLICE;
 import static android.app.slice.SliceItem.FORMAT_TEXT;
@@ -49,6 +51,7 @@
     private SliceItem mSubtitleItem;
     private ArrayList<SliceItem> mEndItems = new ArrayList<>();
     private boolean mEndItemsContainAction;
+    private SliceItem mSlider;
 
     public RowContent(SliceItem rowSlice, boolean showStartItem) {
         populate(rowSlice, showStartItem);
@@ -75,19 +78,22 @@
             return false;
         }
         // Filter anything not viable for displaying in a row
-        ArrayList<SliceItem> rowItems = filterInvalidItems(rowSlice.getSlice().getItems());
+        ArrayList<SliceItem> rowItems = filterInvalidItems(rowSlice);
         // If we've only got one item that's a slice / action use those items instead
         if (rowItems.size() == 1 && (FORMAT_ACTION.equals(rowItems.get(0).getFormat())
                 || FORMAT_SLICE.equals(rowItems.get(0).getFormat()))) {
             if (isValidRow(rowItems.get(0))) {
                 rowSlice = rowItems.get(0);
-                rowItems = filterInvalidItems(rowSlice.getSlice().getItems());
+                rowItems = filterInvalidItems(rowSlice);
             }
         }
         // Content intent
         if (FORMAT_ACTION.equals(rowSlice.getFormat())) {
             mContentIntent = rowSlice;
         }
+        if (SUBTYPE_SLIDER.equals(rowSlice.getSubType())) {
+            mSlider = rowSlice;
+        }
         if (rowItems.size() > 0) {
             // Start item
             if (isStartType(rowItems.get(0))) {
@@ -135,6 +141,14 @@
     }
 
     /**
+     * @return the {@link SliceItem} representing the slider in this row; can be null
+     */
+    @Nullable
+    public SliceItem getSlider() {
+        return mSlider;
+    }
+
+    /**
      * @return whether this row has content that is valid to display.
      */
     public boolean isValid() {
@@ -178,25 +192,25 @@
     /**
      * @return whether this is a valid item to use to populate a row of content.
      */
-    private static boolean isValidRow(SliceItem item) {
+    private static boolean isValidRow(SliceItem rowSlice) {
         // Must be slice or action
-        if (FORMAT_SLICE.equals(item.getFormat()) || FORMAT_ACTION.equals(item.getFormat())) {
+        if (FORMAT_SLICE.equals(rowSlice.getFormat())
+                || FORMAT_ACTION.equals(rowSlice.getFormat())) {
             // Must have at least one legitimate child
-            List<SliceItem> rowItems = item.getSlice().getItems();
+            List<SliceItem> rowItems = rowSlice.getSlice().getItems();
             for (int i = 0; i < rowItems.size(); i++) {
-                if (isValidRowContent(rowItems.get(i))) {
+                if (isValidRowContent(rowSlice, rowItems.get(i))) {
                     return true;
                 }
             }
         }
-        Log.w(TAG, "invalid row content because not a slice or action");
         return false;
     }
 
-    private static ArrayList<SliceItem> filterInvalidItems(List<SliceItem> items) {
+    private static ArrayList<SliceItem> filterInvalidItems(SliceItem rowSlice) {
         ArrayList<SliceItem> filteredList = new ArrayList<>();
-        for (SliceItem i : items) {
-            if (isValidRowContent(i)) {
+        for (SliceItem i : rowSlice.getSlice().getItems()) {
+            if (isValidRowContent(rowSlice, i)) {
                 filteredList.add(i);
             }
         }
@@ -206,7 +220,7 @@
     /**
      * @return whether this item has valid content to display in a row.
      */
-    private static boolean isValidRowContent(SliceItem item) {
+    private static boolean isValidRowContent(SliceItem slice, SliceItem item) {
         // TODO -- filter for shortcut once that's in
         final String itemFormat = item.getFormat();
         // Must be a format that is presentable
@@ -214,7 +228,8 @@
                 || FORMAT_IMAGE.equals(itemFormat)
                 || FORMAT_TIMESTAMP.equals(itemFormat)
                 || FORMAT_REMOTE_INPUT.equals(itemFormat)
-                || FORMAT_ACTION.equals(itemFormat);
+                || FORMAT_ACTION.equals(itemFormat)
+                || (FORMAT_INT.equals(itemFormat) && SUBTYPE_SLIDER.equals(slice.getSubType()));
     }
 
     /**
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 94d16a6..d182dfd 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
@@ -20,9 +20,13 @@
 import static android.app.slice.Slice.HINT_SELECTED;
 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;
@@ -41,6 +45,8 @@
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
 import android.widget.Switch;
 import android.widget.TextView;
 import android.widget.ToggleButton;
@@ -81,6 +87,8 @@
     private View mDivider;
     private ArrayList<CompoundButton> mToggles = new ArrayList<>();
     private LinearLayout mEndContainer;
+    private SeekBar mSeekBar;
+    private ProgressBar mProgressBar;
 
     private int mRowIndex;
     private RowContent mRowContent;
@@ -101,6 +109,8 @@
         mSecondaryText = (TextView) findViewById(android.R.id.summary);
         mDivider = findViewById(R.id.divider);
         mEndContainer = (LinearLayout) findViewById(android.R.id.widget_frame);
+        mSeekBar = (SeekBar) findViewById(R.id.seek_bar);
+        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
     }
 
     @Override
@@ -175,6 +185,12 @@
         }
         mSecondaryText.setVisibility(subTitle != null ? View.VISIBLE : View.GONE);
 
+        final SliceItem slider = mRowContent.getSlider();
+        if (slider != null) {
+            addSlider(slider);
+            return;
+        }
+
         mRowAction = mRowContent.getContentIntent();
         ArrayList<SliceItem> endItems = mRowContent.getEndItems();
         if (endItems.isEmpty()) {
@@ -228,6 +244,48 @@
         }
     }
 
+    private void addSlider(final SliceItem slider) {
+        final ProgressBar progressBar;
+        if (FORMAT_ACTION.equals(slider.getFormat())) {
+            // Seek bar
+            progressBar = mSeekBar;
+            mSeekBar.setVisibility(View.VISIBLE);
+            SliceItem thumb = SliceQuery.find(slider, FORMAT_IMAGE);
+            if (thumb != null) {
+                mSeekBar.setThumb(thumb.getIcon().loadDrawable(getContext()));
+            }
+            mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+                @Override
+                public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+                    try {
+                        PendingIntent pi = slider.getAction();
+                        Intent i = new Intent().putExtra(EXTRA_SLIDER_VALUE, progress);
+                        // TODO: sending this PendingIntent should be rate limited.
+                        pi.send(getContext(), 0, i, null, null);
+                    } catch (CanceledException e) { }
+                }
+
+                @Override
+                public void onStartTrackingTouch(SeekBar seekBar) { }
+
+                @Override
+                public void onStopTrackingTouch(SeekBar seekBar) { }
+            });
+        } else {
+            // Progress bar
+            progressBar = mProgressBar;
+            mProgressBar.setVisibility(View.VISIBLE);
+        }
+        SliceItem max = SliceQuery.findSubtype(slider, FORMAT_INT, SUBTYPE_MAX);
+        if (max != null) {
+            progressBar.setMax(max.getInt());
+        }
+        SliceItem progress = SliceQuery.findSubtype(slider, FORMAT_INT, SUBTYPE_PROGRESS);
+        if (progress != null) {
+            progressBar.setProgress(progress.getInt());
+        }
+    }
+
     /**
      * Add a toggle view to container.
      */
@@ -382,5 +440,7 @@
         mToggles.clear();
         mRowAction = null;
         mDivider.setVisibility(View.GONE);
+        mSeekBar.setVisibility(View.GONE);
+        mProgressBar.setVisibility(View.GONE);
     }
 }
diff --git a/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml b/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml
index 649e39f..7707dae 100644
--- a/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml
+++ b/slices/view/src/main/res/layout-v21/abc_slice_small_template.xml
@@ -56,6 +56,19 @@
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10" />
 
+        <SeekBar
+            android:id="@+id/seek_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+
+        <ProgressBar
+            android:id="@+id/progress_bar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+
     </LinearLayout>
 
     <View
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 e673362..706ded6 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
@@ -55,6 +55,19 @@
             android:textColor="?android:attr/textColorSecondary"
             android:maxLines="10" />
 
+        <SeekBar
+            android:id="@+id/seek_bar"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+
+        <ProgressBar
+            android:id="@+id/progress_bar"
+            style="?android:attr/progressBarStyleHorizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:visibility="gone" />
+
     </LinearLayout>
 
     <View
diff --git a/v7/cardview/Android.mk b/v7/cardview/Android.mk
index 2d9492d..563ea9d 100644
--- a/v7/cardview/Android.mk
+++ b/v7/cardview/Android.mk
@@ -22,6 +22,7 @@
 # in their makefiles to include the resources in their package.
 include $(CLEAR_VARS)
 LOCAL_USE_AAPT2 := true
+LOCAL_AAPT2_ONLY := true
 LOCAL_MODULE := android-support-v7-cardview
 LOCAL_SDK_VERSION := $(SUPPORT_CURRENT_SDK_VERSION)
 LOCAL_SRC_FILES := \
diff --git a/v7/mediarouter/res/values-in/strings.xml b/v7/mediarouter/res/values-in/strings.xml
index e76b3e8..becb41e 100644
--- a/v7/mediarouter/res/values-in/strings.xml
+++ b/v7/mediarouter/res/values-in/strings.xml
@@ -25,7 +25,7 @@
     <string name="mr_chooser_title" msgid="414301941546135990">"Transmisikan ke"</string>
     <string name="mr_chooser_searching" msgid="6349900579507521956">"Mencari perangkat"</string>
     <string name="mr_controller_disconnect" msgid="1227264889412989580">"Putuskan sambungan"</string>
-    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Hentikan cast"</string>
+    <string name="mr_controller_stop_casting" msgid="8857886794086583226">"Hentikan Transmisi"</string>
     <string name="mr_controller_close_description" msgid="7333862312480583260">"Tutup"</string>
     <string name="mr_controller_play" msgid="683634565969987458">"Putar"</string>
     <string name="mr_controller_pause" msgid="5451884435510905406">"Jeda"</string>
diff --git a/v7/preference/res/values/styles.xml b/v7/preference/res/values/styles.xml
index 46351e1..be1797b 100644
--- a/v7/preference/res/values/styles.xml
+++ b/v7/preference/res/values/styles.xml
@@ -84,10 +84,6 @@
 
     <style name="Preference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.Information.Material">
@@ -98,16 +94,10 @@
 
     <style name="Preference.Category.Material">
         <item name="android:layout">@layout/preference_category_material</item>
-        <item name="allowDividerAbove">true</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.CheckBoxPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.SwitchPreferenceCompat.Material">
@@ -116,10 +106,6 @@
 
     <style name="Preference.SwitchPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.SeekBarPreference.Material">
@@ -130,31 +116,18 @@
 
     <style name="Preference.PreferenceScreen.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DialogPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DialogPreference.EditTextPreference.Material">
         <item name="android:layout">@layout/preference_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="singleLineTitle">false</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference.DropDown.Material">
         <item name="android:layout">@layout/preference_dropdown_material</item>
-        <item name="allowDividerAbove">false</item>
-        <item name="allowDividerBelow">true</item>
-        <item name="iconSpaceReserved">true</item>
     </style>
 
     <style name="Preference_TextAppearanceMaterialBody2">
@@ -173,7 +146,6 @@
 
     <style name="PreferenceFragment.Material">
         <item name="android:divider">@drawable/preference_list_divider_material</item>
-        <item name="allowDividerAfterLastItem">false</item>
     </style>
 
     <style name="PreferenceFragmentList.Material">
diff --git a/v7/preference/res/values/themes.xml b/v7/preference/res/values/themes.xml
index 598c24f..1f8f158 100644
--- a/v7/preference/res/values/themes.xml
+++ b/v7/preference/res/values/themes.xml
@@ -51,6 +51,5 @@
         <item name="editTextPreferenceStyle">@style/Preference.DialogPreference.EditTextPreference.Material</item>
         <item name="dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
         <item name="preferenceFragmentListStyle">@style/PreferenceFragmentList.Material</item>
-        <item name="android:scrollbars">vertical</item>
     </style>
 </resources>